Compare commits

..

207 Commits

Author SHA1 Message Date
k88936 8b2d0cb9df 统一错误事件同步留痕到 AppData 应用日志
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m57s
Project CI / Backend tests (pull_request) Failing after 12s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 3m50s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 6m44s
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 7m28s
Project CI / Frontend tests (pull_request) Successful in 4m9s
Project CI / Native shell tests (pull_request) Successful in 8m24s
Project CI / AI game creator shell web tests (pull_request) Successful in 5m37s
- 新增 agent_runtime_error_app_log_lines,把统一错误事件的同一份已脱敏诊断投影成应用日志两行:agent.runtime.error 身份行与 agent.runtime.error.detail 详情行
- 身份行含 eventId/source/stage/code/retryable/clientTurnId/elapsedMs/detailRef/summary,详情行含 hint/detail/metadata;detail 与 metadata 各按 1200/200 字符预算先脱敏再截断,字段仍只来自 sidecar 那份 diagnosis
- 拆两行的原因:整行命中凭据标记会被 sanitize_diagnostic_message 整体替换成脱敏占位,详情行被吃掉时身份行仍能定位 eventId 与 detailRef
- app_log! 先于 sidecar 写入,sidecar 写失败也留下可提交的诊断;各字段先压平换行,保证应用日志逐行读取且 stderr 输出不拆行
- 新增用例覆盖身份字段、凭据脱敏、单行口径与实际 sanitize_diagnostic_message 落盘边界(含超长诊断)
- 同步文件【技术方案】AGC错误报告与诊断上传与【技术方案】AI游戏创作智能体App实施计划,共享记忆 decision-log 与 pitfalls 明确项目内 .agent/runtime/errors sidecar 与进程内错误报告事件池是两套东西,本次只写日志行
- 验证:cargo test runtime_error(7 passed)、cargo fmt --check、npm run check:encoding、git diff --check
2026-09-21 21:34:45 +08:00
kdletters 4d313a44eb 收口后台模板管理里程碑:回填验收证据并删除临时实施计划
Project CI / AI game creator shell Rust crates (push) Successful in 2m58s
Project CI / AI game creator shell Rust smoke (push) Successful in 3m58s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 7m32s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 8m26s
Project CI / Backend tests (push) Successful in 8m33s
Project CI / Repository checks (push) Successful in 4m50s
Project CI / Native shell tests (push) Successful in 9m20s
Project CI / Frontend tests (push) Successful in 5m57s
Project CI / AI game creator shell web tests (push) Successful in 3m19s
- 里程碑 Status 从 approved 更新为 implemented-awaiting-runtime-acceptance,并注明按验收口径未对真实 OSS 写入
- 新增验收证据:合同/权限/领域/存储/CLI 五层用例结果,本地 API 健康检查与未授权读取写入失败关闭,授权读取 9 个模板快照,受控浏览器桌面与 390×844 移动端 smoke
- 新增复审记录:写路径顺序与 409 语义、后台 DTO 结构上无法改动 ZIP/版本、封面公共域名与单 bucket 合同一致、迟到响应判据的覆盖方式
- 按实施计划第 4 步删除临时实施计划文件,里程碑文档保留为唯一验收依据
- 验证:check:doc-index(192 份)、check:encoding、git diff --check
2026-09-21 14:21:25 +08:00
kdletters 01ed9fadc6 修复手工 release 发布未透传 AGC 渠道
Project CI / AI game creator shell Rust crates (push) Successful in 2m45s
Project CI / AI game creator shell Rust smoke (push) Successful in 3m36s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 7m5s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 7m54s
Project CI / Backend tests (push) Successful in 11m10s
Project CI / Repository checks (push) Successful in 6m17s
Project CI / Frontend tests (push) Successful in 8m0s
Project CI / Native shell tests (push) Successful in 11m32s
Project CI / AI game creator shell web tests (push) Has been cancelled
补充手工发布目标到 AGC 更新渠道的踩坑记录

补充 release 客户端包分区与补发流程说明
2026-09-21 14:04:02 +08:00
kdletters 02a7abeb3f 新增后台 AGC 模板管理与模板库内容寻址落库
Project CI / AI game creator shell Rust crates (push) Successful in 2m53s
Project CI / AI game creator shell Rust smoke (push) Successful in 3m42s
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
- 后台新增「模板管理」页:按权限查看并编辑模板名称/简介/标签/封面,支持上架与下架;路由、侧栏、tab 权限、API 客户端与 DTO 同步接入
- api-server 新增 /admin/api/agc-templates 读取与更新路由,复用现有后台鉴权与 tab 权限校验,权限不足按既有失败关闭口径拒绝
- module-assets 收敛模板库纯规则(active/inactive 投影、索引 schema 与重复项校验、ZIP 大小与摘要核对),platform-oss 承担对象存储读写、不可变对象复用与发布互斥锁
- shared-contracts 补后台模板 DTO;spacetime-module 账号存储适配保持既有 schema 不变
- AGC 壳内置模板索引与建项读取改为内容寻址对象,补模板安装、越界归档、索引不匹配等用例
- 模板库发布脚本支持定向发布与上架/下架合并、内容地址复用与锁语义,CLI 守卫用例同步;jenkins 两条客户端打包管线统一触发邮件通知 Job 并传递首装包链接
- 内置 5 个 Cocos 官方模板载荷与索引 fixture(内容地址对象,共 90 个文件)
- 补「后台模板管理」里程碑与实施计划,更新模板库技术方案、开发运维文档与共享记忆
- 验证:admin-web 39 项用例与 typecheck、发布脚本 25 项用例、api-server 与 AGC 壳编译、模板库定向 Rust 用例(AGC 壳 18 项、module-assets/platform-oss 16 项、后台权限 1 项)、check:encoding / check:doc-index / check:production-ops / rustfmt 全部通过
2026-09-21 13:56:20 +08:00
suzmii 6845cdcbc5 修复 Mac 包校验脚本对合法随包 npm 的误判
Project CI / AI game creator shell Rust smoke (push) Successful in 2m14s
Project CI / AI game creator shell Rust crates (push) Successful in 1m26s
Project CI / Frontend tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
- check-macos-bundle 的「包里不得出现 node_modules」门禁只放行了 game-runtime/node/node_modules/npm,但 recursive readdir 会把目录项 game-runtime/node/node_modules 也列出来,于是合法包必然断言失败
- 这条门禁是 #439 引入的,之后每次构建都死在更早的阶段(staging/测试),直到 2026-09-21 arm64 单架构跑通才第一次走到它:12:58、13:24、13:43 三轮都挂在这里
- 修复:放行 npm 目录自身、其子树与它的上级 node_modules 容器目录;已用真实 arm64 产物跑通完整 smoke(资源、架构、摘要、权限、Codex 正式查找、app-server 握手、缺组件拒绝全部通过)
2026-09-21 13:50:32 +08:00
suzmii 4044ffd89b 修复 Mac 发布 Job 手工触发时 Checkout 因未定义参数失败
- 手工「立即构建」不会注入可选参数(COMMIT_HASH / SKIP_IF_SUPERSEDED 未定义而非空值),set -u 下 `[ -n "$COMMIT_HASH" ]` 直接以 unbound variable 退出,Job 2026-09-21 12:54 那一轮就是这样在 Checkout 失败的
- 两段 Checkout 脚本开头统一兜底:SOURCE_BRANCH 默认 master、COMMIT_HASH 默认空、SKIP_IF_SUPERSEDED 默认 false,调度器显式传参时行为不变(仍按 pinned commit 构建并判让位)
2026-09-21 13:50:32 +08:00
suzmii 8d0c021126 修正 Mac 发布文档口径:自动触发与描述原文
- 运维文档原写「不设置 trigger;不接入现有每小时版本调度」,与现状矛盾:调度 Job 会先发号再把 SOURCE_BRANCH/COMMIT_HASH/AGC_UPDATE_CHANNEL/AGC_RELEASE_VERSION/SKIP_IF_SUPERSEDED 透传给 Mac Job,正常发布不需要手工点构建
- 把 Jenkins Job 描述与节点描述的当前原文(arm64 单架构 + 由调度器自动触发 + dry-run 默认关)写进文档,并要求每次改口径同步 Jenkins 侧描述,避免描述停留在 universal/手动触发的旧口径
2026-09-21 13:50:32 +08:00
kdletters 1cb91f9444 修复画布绑定查询与生成失败归因
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
- 画布绑定前置的项目列表查询改为 view=summary,站内与外部路由统一支持摘要视图(缺省 full 兼容,未知取值 400)
- 项目摘要投影收敛到 editor_project,外部 API 与 MCP 复用同一份实现,摘要不做内联媒体修复也不回传画布与全量资源
- 外部编辑器请求失败文案补 kind 语义与底层因链,区分请求超时、正文截断与非法 JSON,不拼接 URL
- 远端资源编辑终态文案带出稳定失败码并指向移出恢复队列这一唯一出口,上游原文仍不写入账本
- 补齐站内摘要视图与画布测试夹具的请求断言用例
- 更新共享决策记录与踩坑记录(图集切片上限条目归上游 #442,本提交不重复)
2026-09-21 13:33:23 +08:00
kdletters 2e31a485d8 修复 Stdb publish 事务确认超时被误判为失败
Project CI / AI game creator shell Rust crates (push) Successful in 3m0s
Project CI / AI game creator shell Rust smoke (push) Successful in 4m0s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 7m41s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 8m44s
Project CI / Backend tests (push) Successful in 8m52s
Project CI / Frontend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
- production-stdb-publish 新增 run_spacetime_publish:仅在出现 timeout waiting for transaction confirmation 时重试一次同版本 publish(SpacetimeDB 对已生效的同版本为幂等),重试成功即视为模块已在位,不再把生产留在维护态
- 其它失败原因保持原有失败关闭语义;生产运维门禁断言补上该重试口径
2026-09-21 13:27:37 +08:00
suzmii 4ad131d74d Merge pull request 'AGC 画布:锚点让位标题栏、生成浮层贴边、拖动误触点击、读秒 100ms 与图集参考口径' (#448) from fix/agc-canvas-bugfix into master
Project CI / AI game creator shell Rust smoke (push) Successful in 3m58s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 7m26s
Project CI / AI game creator shell Rust crates (push) Successful in 1m40s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 8m43s
Project CI / Frontend tests (push) Failing after 5m41s
Project CI / Backend tests (push) Successful in 7m29s
Project CI / Repository checks (push) Successful in 4m50s
Project CI / Native shell tests (push) Successful in 9m32s
Project CI / AI game creator shell web tests (push) Has been cancelled
Reviewed-on: #448
2026-09-21 13:04:11 +08:00
kdletters 886bdc06ed 去掉渠道环境变量用例里重复的 stageRuntime
Project CI / AI game creator shell Rust crates (push) Successful in 2m44s
Project CI / AI game creator shell Rust smoke (push) Successful in 3m37s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 6m56s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 7m58s
Project CI / Backend tests (push) Successful in 6m33s
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
- 两次独立修复(#444 与本仓库 55cb04765)在同一处各加了一个 stageRuntime,合并后成为重复对象键,Web-Build 的 ESLint(no-dupe-keys) 失败
- 保留一条带说明的 stageRuntime stub,用例语义不变
2026-09-21 12:49:34 +08:00
suzmii 562e1ba586 「生成任务」侧栏读秒改为 100ms 粒度:与对话侧共用同一份时钟
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m57s
Project CI / Backend tests (pull_request) Failing after 12s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 4m2s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 7m15s
Project CI / Repository checks (pull_request) Failing after 20s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 8m16s
Project CI / Frontend tests (pull_request) Successful in 7m30s
Project CI / AI game creator shell web tests (pull_request) Successful in 4m1s
Project CI / Native shell tests (pull_request) Successful in 11m1s
- ResourceCanvasAssetGenerationTasksPanelView:删掉面板自带的 1000ms interval 与 nowMillis 状态;两条任务行的「已耗时」下沉为叶子组件(AssetGenerationElapsedLabel / ResourceEditElapsedLabel),各自订阅共享 hook useLiveNow(LIVE_TIMER_TICK_MS = 100)。
- 为什么不是面板级时钟:面板上通常同时挂着多条任务,100ms 的 tick 落在面板上等于每 0.1 秒重建整个侧栏;useLiveNow 的注释明确要求调用方是叶子节点。终态行不订阅,已耗时定格在账本给的结束时间上。
- 行为:不足一分钟的耗时本来就显示到 0.1 秒(formatElapsedDuration),现在每 100ms 真的更新一次,不再是「带小数却 1 秒跳一格」。
- 用例:新增「读秒按 100ms 刷新(0.0 → 0.1 → 0.5 秒)」「终态行不再走表,定格在结束时间」「秒表只订在运行中的那一行上(setInterval 只被调用一次)」;把 LIVE_TIMER_TICK_MS 临时改回 1000 时第一条用例即红,粒度确实被钉住。
2026-09-21 12:48:21 +08:00
suzmii e18067e028 Merge pull request 'macOS 发布改为只出 arm64 单架构(修复 Mac 管线 #7 起连续失败)' (#447) from feat/agc-macos-arm64-only into master
Project CI / AI game creator shell Rust smoke (push) Successful in 2m11s
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
Reviewed-on: #447
2026-09-21 12:38:03 +08:00
suzmii 613d62fdb2 同步里程碑计划:macOS 清单只登记 darwin-aarch64
Project CI / AI game creator shell Rust crates (pull_request) Failing after 53s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m3s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 6m52s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 7m40s
Project CI / Repository checks (pull_request) Failing after 2m0s
Project CI / Backend tests (pull_request) Successful in 7m42s
Project CI / Native shell tests (pull_request) Successful in 8m47s
Project CI / Frontend tests (pull_request) Successful in 6m54s
Project CI / AI game creator shell web tests (pull_request) Successful in 5m1s
- docs/project-memory/plans 的渠道化里程碑仍写着 dev-mac universal 同时写两个平台键,与 2026-09-21 的单架构决策冲突,按当前口径更新并指向决策记录
2026-09-21 12:35:36 +08:00
suzmii b4ce6877c8 修掉「拖动占位卡仍触发点击」的真因:点击抑制的计时起点必须在松手那一刻
- useResourceCanvasGenerationPlaceholders:抑制状态在越过拖动阈值时只登记、不起计时器,改到 pointerup 才起 0ms 计时(deferDragClickSuppressionCleanup);pointercancel 与切项目走 clearDragClickSuppression 立刻收干净。
- 原实现(越过阈值时就起 0ms 计时器)在真实鼠标下不成立:一次拖动里 pointermove 横跨多轮宏任务,计时器早在松手前就把抑制清掉,收尾那次 click 照样生效——jsdom 同步发 move/up 看不见这一档,所以上一版用例是假绿。
- 用例补上真实时序:拖动过程中显式让时钟走一轮宏任务再松手;宿主用例还补上浏览器在 pointerup 之后补发的 lostpointercapture,并断言它不能把这次拖动的抑制一起清掉。
- 敏感性已双向验证:把计时器搬回「越过阈值那一刻」、以及让 lostpointercapture 清抑制,两条用例都会红;改用例前的同步版本对两种错法都会绿(所以上一版没抓住)。
- 注释写明计时起点为什么是松手那一刻,并与资源卡链路的 deferSkippedResourceCardClickCleanup 对齐口径。
2026-09-21 12:34:47 +08:00
suzmii e53992fdc2 macOS 发布改为只出 arm64 单架构
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (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 smoke (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 / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
- build-macos-ci 固定 aarch64-apple-darwin:单架构构建、只跑 arm64 隔离 smoke、首装包命名 <产品名>_<版本>_aarch64.dmg,构建清单记录 smokes 与实际清单平台键
- stage-node-runtime 的 universal-apple-darwin 恢复失败关闭并说明原因:随包 Node 只有单架构官方发行版,按宿主架构放行会让 Intel 上这份侧车不可执行,且通用包自检按 process.arch 校验抓不到
- 守卫用例改为断言入口固定 arm64 目标与 _aarch64.dmg 后缀、universal 目标必须抛错
- Jenkinsfile 与三份技术/运维文档同步单架构口径:清单只登记 darwin-aarch64,Intel 非目标,恢复 universal 的前置条件写在文档里
- 共享记忆补 2026-09-21 决策记录,pitfalls 记下「只带宿主架构的通用包」陷阱,避免再次用过渡实现糊过去
2026-09-21 12:30:56 +08:00
suzmii a5ee06a4c7 Merge pull request '修复macOS主机上AGC发布测试未stub Node运行时staging' (#444) from fix/agc-test-node-runtime-stub into master
Project CI / AI game creator shell Rust smoke (push) Successful in 4m16s
Project CI / AI game creator shell Rust crates (push) Successful in 1m6s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 7m51s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 8m45s
Project CI / Repository checks (push) Failing after 1m55s
Project CI / Backend tests (push) Successful in 6m33s
Project CI / Native shell tests (push) Successful in 7m47s
Project CI / Frontend tests (push) Successful in 5m49s
Project CI / AI game creator shell web tests (push) Has been cancelled
Reviewed-on: #444
2026-09-21 12:23:52 +08:00
suzmii 7b57cba14d 补齐「图集(生成图标素材)为什么没有 @」的当面说明与依据
- 生成浮层:不给 @ 选择器的入口新增一段说明行(`data-resource-canvas-generation-reference-free`),讲清「该入口走平台图集通道,参考只有一张权威规范图(图标规范),不支持另挑参考图」——验收现场那条「生成图标素材没法 @」读起来像漏了按钮,原因其实在通道上。
- 样式:说明行允许折行、沿用次要文字色;与既有的「参考图 n/5」计数行区分开。
- 用例:图集入口断言说明行在场;逐条入口断言「说明行」与「@ 选择器」严格互补;原「不呈现参考选择」的判据从按词匹配改成按计数元素/计数值匹配(说明行本身会提到「参考图」三个字)。
- 文档:入口矩阵补「图集为什么没有 @」一条,写明图集走 `icon-spritesheets` 通道、提交体只有 referenceId + iconDescriptions(无 referenceImageSrcs)、结果绑定硬校验参考恰好一张,以及「平台 OpenAPI 该请求 schema 有 referenceImageSrcs 字段,放开属于产品 + 平台口径变更」。
2026-09-21 12:23:32 +08:00
kdletters 55cb047654 支持 macOS 通用包构建时的 Node 运行时暂存
Project CI / Frontend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
- stage-node-runtime 的 targetRuntime 支持 universal-apple-darwin:在 macOS 宿主上按宿主架构暂存官方 Node(通用包自检按 process.arch 校验),非 darwin 宿主仍失败关闭
- 同步 stage-node-runtime 测试期望:darwin 宿主可用、linux 宿主仍拒绝,Universal Node 合并(lipo)留作未完成项
- 共享记忆记录该限制:当前通用包侧车仍是宿主架构,Intel Mac 需等待真正的通用 Node
2026-09-21 12:15:28 +08:00
suzmii beebcca4de 修复 AGC 画布四项验收问题:锚点让位标题栏、浮层不再撞画布边缘、拖动不再触发点击、图标规范参考口径对齐
- 生成任务锚点新增「资源总览」档:资源总览页仍贴画布顶边内缩 0.85rem,资源栏目画布页让开顶部钉住的整宽栏目标题栏(42px + 0.5rem),两档切换走 margin-top 过渡(减动效下关掉)。
- 生成浮层的顶边与可用高度改为按占位卡的真实屏幕位置算(卡下沿到安全底边),不再假设卡片贴在安全带上沿——占位落到下半屏时浮层底边不再越过画布下沿被切掉。
- 生成浮层新增水平收口:按「浮层左右各留 12px」把锚点收进画布可视范围,卡片靠边时浮层不再有一半落到画布之外;宽度口径与面板 CSS 同源(min(560px, 100% - 24px))。
- 新增画布卡片手势的共享判据 resourceCanvasGestureExceededDragThreshold(阈值仍取 RESOURCE_CANVAS_DRAG_THRESHOLD):资源卡与生成占位卡共用同一份「拖动还是点击」判断。
- 生成占位卡修掉拖动收尾误触点击:越过阈值才算拖动、登记一次性点击抑制(consumeDragClick)由宿主消费,「拖动中」样式也从越过阈值那一刻起才亮。
- 生成占位卡的删除按钮起手手势不再被当成「点卡片」:按起手点判定,松手落回卡片时不触发开合生成浮层。
- 图标规范面板的参考选择注释按现行判据改写(只有图集 icon-spritesheet 不接受用户参考),入口矩阵文档同步补「参考图(@ 引用)」口径并修掉「本地 IPC 没有参考图入参」的旧说法。
- 补齐回归用例:手势模型判据、占位卡拖动/点击与阈值、宿主级「拖完不弹浮层且普通点击仍开合」、浮层顶边与水平收口、锚点分档与跨文件标题栏高度守卫、逐条生成入口的 @ 与判据一致、图标规范面板的 @ 与提交带引用。
- 共享记忆 pitfalls.md 记录两条排障口径:卡类手势必须只有一份阈值与点击抑制判据;画布浮层几何只能按真实屏幕位置算、分页锚点必须让开钉死的标题栏。
2026-09-21 11:58:13 +08:00
kdletters 9bf35e8745 修复 macOS 构建上的渠道环境变量用例宿主依赖
Project CI / AI game creator shell Rust crates (push) Successful in 2m58s
Project CI / AI game creator shell Rust smoke (push) Successful in 3m50s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 7m0s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 8m5s
Project CI / Backend tests (push) Successful in 7m2s
Project CI / Native shell tests (push) Successful in 8m12s
Project CI / Repository checks (push) Successful in 4m8s
Project CI / Frontend tests (push) Successful in 5m15s
Project CI / AI game creator shell web tests (push) Failing after 6m13s
- build-release.test.mjs 的「packaged renderer receives the same channel as the updater manifest」用 windows target 却未注入 stageRuntime,真实 staging 要求宿主 Node 平台/架构与目标一致,导致 macOS(arm64)/Linux 上必然失败
- 该用例只断言传给 spawn 的 VITE_AGC_PLATFORM_CHANNEL,改为注入空 stageRuntime,与同文件其它用例一致
2026-09-21 11:53:32 +08:00
kdletters 90207bc1d5 minimal 备份增加 freeze 副本路径
Project CI / AI game creator shell Rust smoke (push) Successful in 2m35s
Project CI / AI game creator shell Rust crates (push) Successful in 1m14s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 7m4s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 7m28s
Project CI / Backend tests (push) Successful in 7m25s
Project CI / Frontend tests (push) Successful in 7m18s
Project CI / Repository checks (push) Successful in 5m51s
Project CI / Native shell tests (push) Successful in 9m16s
Project CI / AI game creator shell web tests (push) Successful in 3m41s
- database-backup-to-oss.mjs 新增 --freeze-dir:minimal 模式先把选中子集 rsync 成冻结副本(保持原始相对路径)再从副本上传,避免热备时最新 commitlog 持续追加触发 stat 漂移失败
- files-minimal systemd 模板与发布前 minimal 备份默认传入 freeze 目录
- 生产运维门禁同步 freeze 口径断言
2026-09-21 11:37:14 +08:00
kdletters 2f248ea0c3 新增 minimal 备份口径并切换 release 定时备份
Project CI / Frontend tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
- database-backup-to-oss.mjs 新增 --minimal/--retain-snapshots:按上游 retention 语义只备份最近 N 份 snapshot、覆盖最老保留 snapshot 的 commitlog 边界段及其后全部段,外加身份/配置/控制库/模块字节,且不停止任何服务
- 备份检查脚本补充 minimal 计划用例(保留次新+最新 snapshot、丢弃更早 snapshot 与 clog 段、状态路径齐全、保留数校验)
- 发布前备份默认走 minimal 热备(GENARRATIVE_STDB_PUBLISH_BACKUP_MINIMAL=0 可回到冷备),不再需要 44.7GiB 冷备空间
- Server-Provision 新增 DATABASE_BACKUP_PROFILE=files-minimal 与 systemd drop-in,release 推荐使用并继续禁止 files-history
- 运维文档与共享记忆记录上游 retention 依据、实测 40G→3.6G 与不停服热备口径
2026-09-21 11:33:39 +08:00
kdletters 117d482f93 发布前备份空间预检前置并支持自动降级
Project CI / AI game creator shell Rust smoke (push) Successful in 2m13s
Project CI / AI game creator shell Rust crates (push) Successful in 1m7s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 7m9s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 6m57s
Project CI / Native shell tests (push) Successful in 7m58s
Project CI / Repository checks (push) Successful in 5m13s
Project CI / Frontend tests (push) Successful in 7m19s
Project CI / Backend tests (push) Successful in 10m10s
Project CI / AI game creator shell web tests (push) Successful in 4m47s
- database-backup-to-oss.mjs 新增 --check-space-only:archive 按 data×1.1、files 按 max(data×0.05, 2GiB) 计算门槛,空间不足以退出码 3 返回
- production-stdb-publish.sh 在进入维护与停服务之前先做空间预检,archive 不足时自动降级 files(files 不支持 defer,async 收敛为 sync),可用环境变量关闭
- 尚未开始 publish 的失败自动恢复本次停掉的 API/controller/worker 并退出维护,只有真正开始发布之后的失败才保持维护态
- 备份检查脚本与生产运维门禁补上新口径与执行顺序断言,运维文档与共享记忆记录事故与规则
2026-09-21 10:57:29 +08:00
suzmii 9f15c1c572 修复非Windows主机上AGC发布测试未stub Node运行时staging
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m9s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m10s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 6m19s
Project CI / Repository checks (pull_request) Failing after 10s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 7m16s
Project CI / AI game creator shell web tests (pull_request) Successful in 7m2s
Project CI / Frontend tests (pull_request) Successful in 9m46s
Project CI / Native shell tests (pull_request) Successful in 11m46s
build-release.test.mjs的渠道注入用例漏注入stageRuntime,真实staging会用宿主平台与默认Windows目标做一致性校验
该缺陷在macOS主机上必然失败,卡住macOS渠道构建的测试阶段
补上stageRuntime stub,本机(darwin/arm64)验证37项全通过
2026-09-21 10:35:43 +08:00
suzmii 959d367ed4 Merge pull request '图集切片上限对齐平台契约:客户端结果门 64 → 256' (#442) from fix/spritesheet-slice-limit-256 into master
Project CI / AI game creator shell Rust smoke (push) Successful in 2m30s
Project CI / AI game creator shell Rust crates (push) Successful in 1m8s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 5m47s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 6m38s
Project CI / Repository checks (push) Successful in 7m17s
Project CI / Frontend tests (push) Successful in 9m56s
Project CI / Backend tests (push) Successful in 11m6s
Project CI / Native shell tests (push) Successful in 11m58s
Project CI / AI game creator shell web tests (push) Successful in 5m20s
Reviewed-on: #442
2026-09-21 10:27:39 +08:00
suzmii 56dcb06fb8 Merge remote-tracking branch 'origin/master' into fix/spritesheet-slice-limit-256
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m58s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 3m50s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 6m50s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 7m57s
Project CI / Backend tests (pull_request) Successful in 8m54s
Project CI / Native shell tests (pull_request) Successful in 10m16s
Project CI / Frontend tests (pull_request) Successful in 8m14s
Project CI / Repository checks (pull_request) Successful in 7m19s
Project CI / AI game creator shell web tests (pull_request) Successful in 6m56s
# Conflicts:
#	docs/project-memory/shared-memory/pitfalls.md
2026-09-21 10:12:01 +08:00
kdletters f947807fba 修复手动发布的发号 Job Copy Artifact 授权
Project CI / AI game creator shell Rust crates (push) Successful in 2m58s
Project CI / AI game creator shell Rust smoke (push) Successful in 3m56s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 7m17s
Project CI / Backend tests (push) Successful in 6m48s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 9m58s
Project CI / Native shell tests (push) Successful in 8m17s
Project CI / Frontend tests (push) Successful in 5m58s
Project CI / AI game creator shell web tests (push) Successful in 3m35s
Project CI / Repository checks (push) Successful in 4m1s
- 发号 Job 的 copyArtifactPermission 补齐手动发布消费者,用户触发的构建不再报 Unable to find project for artifact copy
- 同步总版本号技术方案与共享记忆排障记录,写明 SYSTEM 定时构建短路、用户触发按认证用户判权的差异与回写 Job property 的步骤
2026-09-21 08:54:17 +08:00
suzmii 1997dfeb80 图集切片上限对齐平台契约:客户端结果门 64 → 256
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m55s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 3m56s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Failing after 7m42s
Project CI / Backend tests (pull_request) Successful in 5m19s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Failing after 8m39s
Project CI / Native shell tests (pull_request) Successful in 7m24s
Project CI / Repository checks (pull_request) Successful in 3m54s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m32s
Project CI / Frontend tests (pull_request) Successful in 4m54s
- 现场(gameagent-6e53c9e8)「AI 生成图标素材」失败:平台已生成并切完图,客户端在绑定结果这一步把整条结果判失败(assetId 为空、付费产物被丢)。
- 根因:同一条链路存在两个上限——平台切分 / Agent 工具 schema sliceCount / 持久化产物批次都是 256,客户端两处结果绑定门仍是 64(external_generation_state.rs、canvas_generation.rs)。
- 修复:两处门统一到 PLATFORM_ART_SPRITESHEET_MAX_SLICES = 256,判据与拒绝文案各只留一份(数字由常量插值),注释点名三处同值权威。
- 用例:新增上限值/边界/文案用例与「64/65/256 片必须能落盘、257 片必须拒」的回归用例;两条都做过变异验证(常量改回 64 即红)。
- 文档:decision-log 与 pitfalls 各补一条(客户端门不得比平台产品上限更严;上限引同一常量)。
2026-09-21 08:31:39 +08:00
kdletters 0ac86f941c 优化 Gitea CI Rust 分片并发
Project CI / AI game creator shell Rust crates (push) Successful in 53s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m58s
Project CI / AI game creator shell Rust lane 2/2 (push) Failing after 6m40s
Project CI / AI game creator shell Rust lane 1/2 (push) Failing after 9m54s
Project CI / Frontend tests (push) Failing after 5m2s
Project CI / Backend tests (push) Successful in 8m9s
Project CI / Repository checks (push) Successful in 4m7s
Project CI / Native shell tests (push) Successful in 8m58s
Project CI / AI game creator shell web tests (push) Successful in 3m9s
将四个 AGC Rust shard 合并为两条 lane,减少重复预热与排队

同步 workflow 契约测试、分片说明和运维文档
2026-09-21 07:56:06 +08:00
suzmii 3815d00c84 Merge pull request 'AGC 画布三项:生成任务侧栏右上角 / 拖到对话批量引用 / 替换面板画布点选' (#441) from feat/agc-canvas-ux-followups into master
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Reviewed-on: #441
2026-09-21 07:51:07 +08:00
suzmii 4172534bab Merge branch 'master' into feat/agc-canvas-ux-followups
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 7m14s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 7m44s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 7m46s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m2s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m7s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 9m41s
Project CI / Repository checks (pull_request) Successful in 3m44s
Project CI / Backend tests (pull_request) Successful in 6m4s
Project CI / Frontend tests (pull_request) Successful in 5m6s
Project CI / Native shell tests (pull_request) Successful in 9m0s
Project CI / AI game creator shell web tests (pull_request) Failing after 5m20s
2026-09-21 07:51:00 +08:00
kdletters ab9820990c 补充 Windows 构建节点 Python 安装口径
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 7m28s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 7m55s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 8m2s
Project CI / AI game creator shell Rust crates (push) Successful in 1m8s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m21s
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 10m4s
Project CI / Frontend tests (push) Failing after 4m17s
Project CI / Repository checks (push) Successful in 4m21s
Project CI / Backend tests (push) Successful in 6m49s
Project CI / Native shell tests (push) Successful in 7m48s
Project CI / AI game creator shell web tests (push) Successful in 2m58s
- 排障记录写明新节点静默安装 Python 3 的确切命令,并说明 CMake 用 Build Tools 自带版本
2026-09-21 06:58:45 +08:00
kdletters 510e550f19 补充 Windows 构建节点修复的实跑证据
- Godot 编辑器插件接入文档记录 Jenkins Windows 节点在本修复分支上的 dry-run 实跑结果:preflight、原生载荷 staging、NSIS 安装包与渠道清单
2026-09-21 06:58:39 +08:00
kdletters 6dadd76c1b 修复 Windows AGC 构建节点的 Godot 原生扩展工具链
- AGC_WINDOWS_PATH 增加 Build Tools 自带 CMake 与 Python 3.12 目录,阶段 PATH 不再漏掉 Godot 原生扩展所需工具
- Toolchain preflight 校验 CMake 3.25 及以上、Python 3 和 Visual Studio 17 2022 生成器,缺失时在构建前失败关闭
- 同步 Godot 编辑器插件接入文档与共享记忆排障记录
2026-09-21 06:41:03 +08:00
suzmii 1a507dbf2d Merge remote-tracking branch 'origin/master' into feat/agc-canvas-ux-followups
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 6m50s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 5m29s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 41s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 10m19s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 5m17s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m2s
Project CI / Repository checks (pull_request) Successful in 4m57s
Project CI / Frontend tests (pull_request) Successful in 6m13s
Project CI / Backend tests (pull_request) Successful in 7m40s
Project CI / Native shell tests (pull_request) Successful in 8m27s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m57s
2026-09-21 06:21:38 +08:00
suzmii 5467ed6d7b 自查修正:拖到对话落点逐值比较、替换面板锚进画布容器并与任务面板互斥
- 拖动落点改逐值比较 sameResourceCardReferenceDrop(条数 + 对话栏矩形四值),指针在对话栏上移动不再每帧 setState、不再每帧重渲染工作台。
- 替换面板从 stage 顶层移进画布容器 .game-resource-book-manager(本身 position: relative),top 3.2rem 排在任务开关下方;相对 stage 写死的偏移会在工具条换行时压上去,与刚修的任务开关是同一类隐患。
- 打开替换会话时收起「生成任务」面板:画布右上角同一时刻只留一块浮层(任务照旧在账本里推进)。
- 面板上方补一行「替换源素材:<显示名>」:非模态之后画布上的源素材光环会被空白点击清掉,源身份得在面板这一侧留一份可读说法。
- 测试:替换用例新增「面板锚在画布容器里、写明源素材、并与任务面板互斥」(20 passed)、落点模型新增逐值比较用例(6 passed)。
- 文档:decision-log 新增本批自查条目、待办文档补登记、验收用例 C9 注脚改为 nonModal 形态。
2026-09-21 06:21:19 +08:00
kdletters 4b788fa73a 修复 Gitea CI 镜像构建并刷新到 1.98.1 工具链 (#438)
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 7m25s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 7m39s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 7m48s
Project CI / AI game creator shell Rust crates (push) Successful in 1m11s
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 9m55s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m29s
Project CI / Repository checks (push) Successful in 4m9s
Project CI / Backend tests (push) Successful in 6m18s
Project CI / Frontend tests (push) Successful in 6m17s
Project CI / Native shell tests (push) Successful in 9m22s
Project CI / AI game creator shell web tests (push) Successful in 5m21s
修复 CI job 镜像无法构建的问题,并换用 Rust 1.98.1 工具链镜像,跑通 PR 门禁。

Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/438
Co-authored-by: kdletters <kdletters@qq.com>
Co-committed-by: kdletters <kdletters@qq.com>
2026-09-21 06:15:57 +08:00
suzmii 7148ff9eaf 验收修正:任务锚点改挂画布那一格,展开后开关让位
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 7m22s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 7m32s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 7m46s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 7m50s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m10s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 3m8s
Project CI / Backend 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 / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
- 重叠:右上角锚点不再用 position: absolute + 写死的 top(工具条是 stage 第一行、高度随按钮换行变化),改为 stage 网格里与工作面同一个单元格的条目:grid-row 按 placement 分档 + grid-column 1 + justify-self end + align-self start + margin;同格的三处容器也显式钉住第 1 列,避免画布被自动列放置挤进隐式第二列。
- 并排:开关只在完全收起时渲染(收起动画期间也不画),展开态画布右上角只留面板,收起走面板头部 × 或点画布外部。
- 测试:面板用例新增「展开时开关让位」、计数用例拆成收起查开关 / 展开查面板;样式用例改判网格条目坐标与窄屏 justify-self: stretch;dismiss 用例改走面板 × 收起、收起后开关回来能再打开。
- 文档:PRD 3.10 与更新时间、decision-log 新增 2026-09-21 验收修正条目、待办文档补登记该修正。
2026-09-21 04:33:26 +08:00
suzmii b20d980ba7 替换点选判据的注释口径跟上新流程
- resolveResourceReplacementPick 的注释里「点选态」「点选替换」改为「画布点选」「面板里的当前替换目标」,与退役掉的按钮 / 提示条口径一致;只改注释,行为不变。
2026-09-21 03:53:07 +08:00
suzmii de502e9bc3 替换面板改为非模态浮层,支持在画布上点选替换目标
- 共享选择组件新增 opt-in nonModal:不铺遮罩、不做焦点陷阱、限高 + 内部滚动,Esc 在 document 阶段截断后取消,关掉即卸载;网页端美术画布不传,弹窗行为逐字不变。
- 新增 initialSelectionRevision:面板开着时宿主换了目标才重同步当前选择,面板里的搜索词与分类筛选不被重置。
- AGC 侧把候选面板锚到画布右上角(任务开关下方),壳样式放共享样式表、宿主只负责锚定;面板开着时画布照常可点,点中合法候选落成面板当前选择,写入仍只由「确认」发起。
- 退役旧的「点选替换」按钮、画布提示条与 resourceReplacementPickMode:同一个功能不留两条 UI 路径;合法性判据仍复用 resolveResourceReplacementPick,写入仍是同一个 confirm 函数。
- 确认时用画布点选结果兜底,避免点完当帧按确认误报「请选择一个替换素材」。
- 测试:替换用例 4 条点选路径改写为非模态 + 面板点选(含四类非法目标在面板内报因、Esc 只收面板),新增「画布点选不清掉面板搜索/分类」;共享组件用例新增 nonModal 形态与浮层壳声明级契约。
- 文档:PRD 5.3 / 7.8 第 8 条改写、验收用例 S15a 改写、decision-log 新增 2026-09-21 条目、待办文档登记三项并勾掉对应需求。
2026-09-21 03:48:40 +08:00
suzmii 9639a080c6 资源卡拖到对话栏实现批量 @ 引用
- 新增落点模型 resourceCardReferenceDropModel:对话栏矩形量取(0 x 0 判成没有落点,避免画布内拖动被误判)、命中判据、引用构造与落点提示文案。
- 引用构造与工具条「引用」按钮逐字一致(只认已登记 manifest 素材、source 为 resource-card、kind/分类/标签取自资源投影),一条都构造不出来时用提示条说明原因。
- 拖动中指针落到对话栏上即从排版切到引用:卡片不再跟指针走,改铺虚线落点浮层并提示「松手即可 @ 引用 N 项素材」;拖回画布内松手仍是原来的排版语义。
- 批量范围与拖动位移同一个集合(多选整批、单选只引用按住的那张);新增批量事件 RESOURCE_REFERENCE_INSERT_MANY_EVENT,App 侧一次插入整批并只聚焦一次,空批次不派发。
- 对话栏挂 ref(不 document.querySelector)、落点浮层按对话栏当时的屏幕矩形铺在根节点上,不改对话栏自身的定位与排版。
- 测试:新增落点模型用例 5 条与画布集成用例 4 条(单卡/多选/拖回画布/pointercancel),resourceReferenceInput 新增批量事件与一次插入整批 chip 两条,共 39 条定向全绿。
- 文档:功能说明新增「拖拽引用」一节与完成清单条目、PRD 更新时间、decision-log 新增 2026-09-21 条目。
2026-09-21 03:37:43 +08:00
suzmii d56b5ff15a AGC「生成任务」侧栏移到画布右上角:开关常驻、面板贴着它展开
- 新增右上角锚点 game-resource-generation-tasks-anchor,挂在 game-workbench-stage 内;资源栏目画布与 UI 编辑器用画布顶边那一档,运行表现层下移让开右上角的版本入口。
- 开关改由 ResourceCanvasAssetGenerationTasksPanelView 自己渲染(保留 data-resource-generation-task-toggle 与计数属性、可访问名唯一),收起态只剩这一枚开关;工具条上两处重复入口与宿主那份 resourceAssetGenerationInFlightCount 一并删除。
- 面板自己不再定位,改由 max-height 封顶 + 内部滚动;进场 / 退场动画位移方向跟着锚点翻到正 X。
- 开关沿用原工具条那枚次级胶囊外观,颜色 / 圆角 / 字重 / 阴影继续全部走平台 token。
- 测试:面板用例新增「折叠只剩右上角开关」「开关计数与面板头部同源」「锚点按工作面分档」;样式用例改为锚点坐标 / 运行档下移 / 面板不再自定位 / 开关走 token;dismiss 用例改为右上角入口且工具条不再有生成任务按钮。
- 文档:PRD 3.10 与更新时间、decision-log 新增 2026-09-21 条目、画布验收待办登记第四批并勾掉该条。
2026-09-21 03:25:53 +08:00
kdletters 485ed50b26 AGC 七项交付效率优化并升级捆绑 Codex 到 0.155.1 (#439)
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
## 背景

净月潭案例(2026-09-19 21:53 → 09-20 01:26,3 小时 33 分)的问题不是模型慢,而是环境未就绪、验收靠模型自述、预算只约束单个工具入口、工具调用被 SDK 全局串行闸门卡住。本 PR 落地确认后的七项交付效率合同,并把捆绑 Codex 升到当前 npm latest。

## 结果

- 新建 Web 游戏在正式生成前由宿主自动预检:捆绑 Node/npm、真实 Vite 构建、受限浏览器桌面/移动截图;失败不启动生成或付费素材。
- 首个副作用前冻结交付合同,宿主保存权威证据与预算;证据齐全后先封口、排空并取得执行器完整退出证明,再产出交付报告,模型回复不再当作验收。
- 原生 shell、内置浏览器与托管命令、第三方 MCP 共用同一执行许可和累计执行时间;`validation.maxRuns` 改为执行/返修批次语义,另设 `maxTurnSeconds` 墙钟上限。
- 所有工具可并发:移除 SDK 全局串行的 `apply_patch`/`update_plan` 注册,改由宿主 `agc_apply_patch`(官方 parser + 当前回合写许可 + 受控进程树 + 短项目事务)和 `agc_update_plan` 提供等价能力;真实请求目录里已无串行注册,长 MCP 与补丁/计划实测在同一响应内重叠。
- 付费提交与本地写入绑定原回合原租约:容量与同动作锁等待可取消,封口、取消或预算耗尽后零新增提交;已越过提交边界的请求保留 operation ID 走 GET 对账,不自动重放。
- 新增请求与工具分段计时、有界并行批读和首轮上下文预取,未知耗时不补零。
- 捆绑 Codex 0.147.0 → 0.155.1:固定版本收敛到 `build_support/codex_bundle.rs` 单一声明,vendor 解析源码按 `rust-v0.155.1` 逐字节重取并更新 UPSTREAM 证据,适配 0.155 统一 exec(`exec_command`/`write_stdin`);macOS 侧车最小系统版本仍为 15.0。

## 验证

- 生产 CLI → 捆绑 Codex 0.155.1 → 本地 Responses/MCP 夹具 9/9:补丁、完成收尾、批次、只读/可写 MCP 并发、原生命令并发、原生资源并发、统一 exec 会话、期限终止(真实重叠 775 / 764 / 999 ms)。
- 真实模型目录 2/2;vendor 上游库 111 项;宿主定向与回归 106 项;真实 Windows 进程与取消 21 项;Node 侧门禁 52 项。
- 发行载荷:artifact-only 重建 NSIS,解包验证侧车清单 `codex-cli 0.155.1`、6 个组件哈希、打包后 `bin/codex.exe --version`、Node 运行时 2130 文件与双端 PNG、`--environment-check` ready。
- `check-config`、TypeScript、编码(4991 文件)、文档索引、`git diff --check`、`cargo fmt --check` 全部通过。

## 未覆盖

- 真实陶泥儿登录态 Provider 生成尚未执行(本机 `--llm-status` 返回 `authentication-required`)。
- macOS 侧车只在本机做静态 Mach-O 与清单解析,未在 macOS 上跑 `check-macos-bundle.mjs`。
- 全仓库聚合套件在本机仍因负载敏感的后台 mock-provider 用例而红,与本次改动无关:改动前的旧二进制同样失败,换单线程后相关用例 3/3 通过。

---------

Co-authored-by: kdletters <61648117+kdletters@users.noreply.github.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/439
2026-09-21 02:29:29 +08:00
suzmii 1655a2eab4 Merge pull request '画布验收遗留问题与后续需求' (#419) from fix/agc-acceptance-followup into master
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 6m39s
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 8m5s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 5m15s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m40s
Project CI / AI game creator shell Rust crates (push) Successful in 1m3s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 5m2s
Project CI / Backend tests (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
2026-09-21 02:02:19 +08:00
kdletters df39d5a16f 修复客户端渠道与错误报告展示
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
客户端 debug 保留服务器选择,正式包按发布渠道连接服务

修复错误报告时间解析与超时路由脱敏

重做后台错误详情面板并补齐定向测试

同步渠道、诊断与后台展示文档
2026-09-21 01:52:35 +08:00
kdletters 914791d63c 修复项目列表状态互相污染
保留已确认项目的独立状态

隔离项目检查代次和迟到结果

增加坏项目刷新回归测试并同步项目文档
2026-09-21 01:48:19 +08:00
suzmii 5574f41129 文档同步:PR #419 合并前复核结论与全量测试口径
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 6m51s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 7m11s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 7m23s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 8m3s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m15s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m49s
Project CI / Repository checks (pull_request) Successful in 4m9s
Project CI / Frontend tests (pull_request) Successful in 6m11s
Project CI / Backend tests (pull_request) Successful in 7m45s
Project CI / Native shell tests (pull_request) Successful in 10m11s
Project CI / AI game creator shell web tests (pull_request) Successful in 5m21s
- 待办文档补「合并前复核(2026-09-21,PR #419)」:仓库根全量 373 文件 / 4512 通过 / 34 跳过 / 0 失败、No Conflicts、范围核对(后端与契约零改动)
- decision-log 的验证段补同一口径;同时记下未复验的两类(真实客户端观感、远程 CI)都不阻塞合并
2026-09-21 01:45:18 +08:00
suzmii cd98e0603e 合并 master:音频后台化与 Mac 构建管线进分支,冲突按新口径解
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (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 shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
- index.tsx 的 audio/video 提交:取 master 侧(#437 已把它改成后台任务队列 + 项目内账本),
  卡片浮层那套本地提交记录在这条链路上不再需要——队列本身就是它的可见性与幂等来源
- PRD 更新时间两边的两条事实合成一行;decision-log 保留 master 的音频章节与我们的
  派生任务/提交即关章节,并把「appSurface 16 条既有失败」更正为 cwd 假红的口径
- 样式守卫里撤掉「生图面板」这条探针:它走 AGC 自有类名,本就不吃
  `image-canvas-editor__*` 那一族(守卫自身加的空集断言把这条假探针暴露了出来)
- 合并后从仓库根验证:appSurface 538 通过 / 17 跳过 / 0 失败;AGC 定向 84 条、typecheck、
  check:encoding、git diff --check 全通过
2026-09-21 01:33:06 +08:00
suzmii 4bc0f04d74 补齐画布验收待办:review 修复与生图浮层美化的进展记录
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (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 / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (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
- 记下在途拦截补齐生成动画、样式守卫扩到三面板、窄屏差异显式放行、生图浮层紧凑化与基类 token
- 更正「appSurface 16 条既有失败」的口径:那是从 apps 目录跑造成的 cwd 假红,仓库根跑为 0 失败
2026-09-21 01:24:56 +08:00
suzmii 329dd749eb 美化生图浮层:紧凑排布不再出现滚动条,画布卡片外形收敛到基类 token
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (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 / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (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
- 字段行改两列(标签在左、控件在右),比例与尺寸并排一行,提示词 6 行降到 3 行
- 润色入口、参考图计数与取消 / 生成两个按钮收进同一行动作:原先它们各占一行把内容顶到
  几何上界之外,面板因此出现滚动条;重排后面板高度回落到上界以内(overflow 仅作兜底)
- 新增画布卡片基类 token(.game-project-workbench 上的 --game-canvas-card-*),取值照抄
  网页端美术画布的面板;占位卡与生成浮层/弹窗从此共用一套圆角、描边、底色与投影,各卡只表达差异
- 用例补一条结构回归:动作行同时装下润色 / 参考计数 / 两个动作,比例与尺寸同处一格
2026-09-21 01:24:03 +08:00
suzmii 2a2e4c1cea 按 review 修:动画在途拦截、样式守卫扩到三面板、窄屏差异显式放行、用例不再依赖 cwd
- 在途拦截补齐生成动画:提交记录的「属于哪张资源」从 draftKey 拆成 resourcePath,
  `resourceCanvasResourceEditSubmissionBlocksResource` 被快速编辑与生成动画共用;
  此前动画这条键对不上、拦不住,重开面板再提交会复用同一 operationId 卡在原生锁上,
  或(改了提示词)直接多出一笔付费生成
- 样式缺口守卫扩到三块面板(动画面板 / 快速编辑面板 / 生图面板),判据改为按
  「选择器分支 + 声明」逐条比对;两个面板的实测缺口一并照抄补上(快速编辑面板的
  `overflow: auto → visible` 与窄屏底栏两条按宿主差异显式登记,不静默漂移)
- 窄屏只搬动画面板那一支:网页端 ≤760px 会把快速编辑浮层也变成整宽底栏,AGC 的
  快速编辑是「居中贴资源卡」的绝对定位,跟随它会压住栏目工具栏与任务侧栏
- 用例路径不再依赖 cwd:新增 tests/repoPath.ts(按 import.meta.url 反推仓库根),
  19 个用例文件的 `resolve(process.cwd(), …)` 统一改用它;顺手修掉
  `HOST_STYLE_SHEETS.filter(existsSync)` 把仓库相对路径交给 cwd 解析的假红
- 清掉两处死代码:不可达的 `quickEditPanel.status === 'generating'` 判据与
  未被使用的 prune 导出
2026-09-21 01:23:35 +08:00
suzmii 12c1e5f3f8 Merge pull request '接入 Mac universal 构建管线与 Jenkins 归档 Job' (#430) from feat/jenkins-mac-build into master
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 7m37s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 8m0s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 8m1s
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 8m51s
Project CI / AI game creator shell Rust crates (push) Successful in 1m20s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m15s
Project CI / Repository checks (push) Successful in 4m4s
Project CI / Frontend tests (push) Successful in 5m11s
Project CI / Backend tests (push) Successful in 6m35s
Project CI / Native shell tests (push) Successful in 8m41s
Project CI / AI game creator shell web tests (push) Successful in 4m44s
Reviewed-on: #430
2026-09-21 01:02:04 +08:00
suzmii b178431a57 Merge branch 'master' into feat/jenkins-mac-build
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 21s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 21s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 21s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 21s
Project CI / Native shell tests (pull_request) Failing after 20s
Project CI / Backend tests (pull_request) Failing after 20s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 20s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 20s
Project CI / Frontend tests (pull_request) Failing after 6s
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / AI game creator shell web tests (pull_request) Failing after 12s
2026-09-21 00:54:24 +08:00
suzmii 65b7ae0e2b 修正Mac构建入口的个人路径、超时余量与过期文档口径
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 15s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 15s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 15s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 11s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 18s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 19s
Project CI / Native shell tests (pull_request) Failing after 18s
Project CI / Backend tests (pull_request) Failing after 19s
Project CI / Frontend tests (pull_request) Failing after 7s
Project CI / AI game creator shell web tests (pull_request) Failing after 11s
Project CI / Repository checks (pull_request) Failing after 11s
Jenkinsfile的PATH改为按$HOME展开,移除写死的节点用户名
构建超时90分钟提高到150分钟并补失败/中断诊断,避免冷构建被中断在链接阶段
同步更新检查技术方案里dry-run默认值与调度接入的过期描述
新增回归断言:禁止个人Home路径、锁定超时余量与诊断文案
2026-09-21 00:48:55 +08:00
k88936 4be95f50ae Merge pull request 'Fix/let bgm and sfx async' (#437) from fix/let-bgm-and-sfx-async into master
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 15s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 12s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 16s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 16s
Project CI / AI game creator shell Rust smoke (push) Failing after 17s
Project CI / AI game creator shell Rust crates (push) Failing after 13s
Project CI / Frontend tests (push) Failing after 8s
Project CI / Backend tests (push) Failing after 16s
Project CI / Native shell tests (push) Failing after 16s
Project CI / Repository checks (push) Failing after 14s
Project CI / AI game creator shell web tests (push) Failing after 12s
Reviewed-on: #437
2026-09-21 00:48:49 +08:00
k88936 f19a003dec Merge branch 'master' into fix/let-bgm-and-sfx-async
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 17s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 17s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 18s
Project CI / Backend tests (pull_request) Failing after 17s
Project CI / Native shell tests (pull_request) Failing after 17s
Project CI / Frontend tests (pull_request) Failing after 8s
Project CI / AI game creator shell web tests (pull_request) Failing after 15s
Project CI / Repository checks (pull_request) Failing after 15s
2026-09-21 00:48:26 +08:00
suzmii 8cffbf6112 回退合并时被钩子误格式化的无关文件
过程中发现并修复:本地 pre-commit 钩子在提交合并时pipeline里prettier会重排无关文件,导致本PR携带与客户端无关的上下文
本提交把20个纯格式化文件恢复为主线内容,PR只保留macOS构建管线和发布链路相关改动
后续提交不再把这些文件纳入范围
2026-09-21 00:47:05 +08:00
suzmii eed72ba39a Merge branch 'master' into fix/agc-acceptance-followup
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 19s
Project CI / Backend tests (pull_request) Failing after 18s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 18s
Project CI / Native shell tests (pull_request) Failing after 18s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 18s
Project CI / Frontend tests (pull_request) Failing after 7s
Project CI / AI game creator shell web tests (pull_request) Failing after 12s
Project CI / Repository checks (pull_request) Failing after 12s
2026-09-21 00:38:08 +08:00
suzmii aa3e29f04e 修掉生图弹窗点输入框就弹素材选择框:label 转发点击到 @ 按钮
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (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 shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
- 现象:点生成提示词输入框任意位置都会弹出「选择素材」。根因是这一格用 `<label>` 包住,而 `<label>` 会把点击转发给内部第一个可标注控件——正好是引用输入区里的「插入素材引用」按钮
- 处理:该格改为 `<div>` 包裹;可访问名仍由控件自身的 aria-label 提供(引用输入区 `ariaLabel` 与 `PlatformTextField` 的 `aria-label`),不依赖 label 关联
- 用例:`resourceCanvasAssetGenerationReferences` 新增「点提示词输入区不该自己弹出素材选择框,只有 @ 按钮才开」——先用旧代码复现出红灯再修
- 顺带扫过资源工作台其余 `<label>`:都只包纯 input / textarea(音频与视频生成面板用纯 textarea,没有 @ 按钮),无同类隐患
- 待办文档补「2026-09-21 第三批」:卡片浮层提交即关、动画面板样式整档照抄与守卫升级、本条 label 修复
2026-09-21 00:37:08 +08:00
k88936 04db93faab 给生成面板的「提交被拒 + 草稿归属」加 TODO(只留标记,不改行为)
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 17s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 17s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 18s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 19s
Project CI / Backend tests (pull_request) Failing after 18s
Project CI / Native shell tests (pull_request) Failing after 18s
Project CI / Frontend tests (pull_request) Failing after 8s
Project CI / Repository checks (pull_request) Failing after 16s
Project CI / AI game creator shell web tests (pull_request) Failing after 16s
- `ResourceCanvasGenerationPanelView.submit()` 加 `TODO(resource-generation-panel-contract)`:说明
  `onSubmit` 只返回 `void` 导致宿主同步拒绝时草稿没人接走的缺口,以及「`onSubmit` 回报受理结果、
  只有受理成功才置位 `draftReleased` 并关闭」的修法
- `ResourceCanvasAssetGenerationPanelView.submit()` 加同名 TODO:图片类面板与音频面板是同一份
  契约,要改一起改
- `index.tsx` 的 `submitResourceCanvasGeneration` 同步拒绝分支加同名 TODO:指向面板侧修法与
  「这里改成 `return false`」的宿主侧动作
2026-09-21 00:29:55 +08:00
suzmii 55259683b8 删除角色动画面板里重复回显提示词的「动画描述」行
- 共享组件 ImageCanvasCharacterAnimationPanelView 不再渲染只读的生成文本回显(与上方输入区内容重复,且两个宿主都没有为它定义样式)
- 用例同步:原断言「生成文本:…」改为钉住该节点不再渲染,并保留「输入区确实收下这段文本」的检查
2026-09-21 00:28:26 +08:00
suzmii 5b4f9e961d 按美术画布原样补齐动画面板样式:关闭键落位、提示词态与 760px 整档
- 补 `.image-canvas-editor__generation-close` 的第二条同名规则(浮层内绝对定位):上一版只搬了基础外观,关闭键因此掉进网格流、独自占一行并居中
- 补提示词输入区的聚焦态与 aria-invalid 两档,以及 composer 内参数簇的透明底色与 hover / 展开态
- 新增 `@media (max-width: 760px)` 整档照抄(浮层落底部整宽、composer 定宽、动作竖排、参数与提交整宽),替换上一版按 720px 手写的那一小段,选择器与断点都与网页端逐字一致
- 守卫升级为「选择器 + 声明」逐条比对:同名规则只搬第一条不再判过;范围限定在 `image-canvas-editor__` 命名空间(共享 chrome 类与全局 reset 不归宿主 chrome 表照抄)
2026-09-21 00:26:31 +08:00
k88936 98e42e6dae 文档同步:音频无源生成的源媒体类型由 editKind 推出,不作为入参
- `docs/technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md`:音频入口复用无源生成链路的措辞
  从「传 `sourceMediaType: 'audio/mpeg'`」改成「源快照媒体类型由 `editKind` 推为 `audio/mpeg`
  (Create 模式不读该入参)」
- `docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md` 第 7.9 节第 6 条同口径修正,避免文档继续
  描述一个代码已经不传、也不会被读的参数
2026-09-21 00:21:07 +08:00
suzmii 973e965f4f 卡片浮层改为提交即关:快速编辑 / 生成动画只负责交任务
- 快速编辑与生成动画点提交当帧即关面板(不等 IPC、不留等待态),进度、阶段与结果只由「生成任务」侧栏承载,与图片类生成面板同一口径
- 受理之后的失败不再重开面板:侧栏那条收口为失败 + 提示条,草稿回落成「未完成编辑」等用户重开;只有点击瞬间就失败(名称预检、源素材不在画布、无客户端)留在面板里
- 派生请求身份改为按「入口 + 资源路径」记忆:重开面板再提交时提示词没变就沿用同一 operationId / 幂等键,命中同一 operation 账本;派生成成功后清除
- 删除 canDismissResourceCanvasQuickEdit 与宿主里只为它服务的两块面板状态 ref,clearResourceCanvasFocus 回到「清选中 + 关两块浮层」的直线逻辑
- 三条既有用例按新口径改写为「重开面板再重试,身份不变」;PRD §3.10 与 decision-log 同步
2026-09-21 00:19:42 +08:00
k88936 f2a3ba7aca 音频提交上下文按任务 id 各自留存,重叠提交不再互相覆盖
- `resourceGenerationAudioSubmissionRef` 从单槽改成按任务 id 索引的 Map(键是 task id,值里
  不再重复存 taskId)
- 提交写入改成 `set(task.taskId, …)`,收口读取改成 `get(settlement.taskId)` + `delete`,切项目
  改成 `clear()`
- 原先两条音频提交重叠时,后一次提交会覆盖前一条的提交上下文,前一条「后端从未受理」的即时
  失败因此找不到自己的恢复上下文,只走通用失败提示、丢掉「面板连原草稿带回来」
- 新增宿主用例「两条音频提交重叠:第一条未被受理时,它的面板仍会被带回来」:给测试宿主加
  `holdFirstAssetGenerationStart` 开关,把第一次派发挂在受理前,复现两条提交重叠
2026-09-21 00:17:05 +08:00
k88936 7b1bc1d3e8 生成任务先登记 live 再落账本,关掉被判成中断残留的窗口
- `start_local_project_asset_generation` 的音频分支改为一律先登记 live、再写排队记录(原先顺序
  相反,中间窗口里并发 `list_local_project_asset_generations` 会把这条排队记录当成上次运行的
  残留收口成失败)
- 图片类分支同一处顺序问题一并改正:同一条命令的两条通道共用同一条顺序约束
- 新增 `register_live_task_id`:登记并返回「本轮是否真的插入了新 id」,落账失败时只回滚本轮插入
  的那条登记,避免删掉同 id 那个正在运行任务的 live 登记
- 新增用例 `a_failed_ledger_write_only_takes_back_the_live_registration_it_inserted` 覆盖该回滚判据
2026-09-21 00:14:11 +08:00
k88936 ae0ab0ddf4 音频后台生成不再传形同虚设的 sourceMediaType
- `run_local_project_audio_generation_at` 提交 `derive_local_project_resource_at` 时把
  `source_media_type` 从 `Some("audio/mpeg")` 改成 `None`(`generation_mode: Create` 下源快照的
  媒体类型由 `edit_kind` 推出,这条通道根本不读该入参,留着会让读者以为它有作用)
- 该函数文档补一句说明「Create 模式不读 `input.source_media_type`」,避免后续又被加回去
2026-09-21 00:11:31 +08:00
k88936 fca111239c AGC 音频账本 kind 判定改成显式穷举,去掉静默兜底
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 20s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 20s
Project CI / Native shell tests (pull_request) Failing after 17s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 19s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 19s
Project CI / Backend tests (pull_request) Failing after 18s
Project CI / Frontend tests (pull_request) Failing after 8s
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / AI game creator shell web tests (pull_request) Failing after 12s
- `begin_local_project_audio_generation_task` 的 `edit_kind → 账本 kind` 映射显式列出
  `SoundEffect` 与 `BackgroundMusic` 两个成员,不再用 `_ =>` 兜底
- 其余 `LocalProjectResourceEditKind` 变体在此处直接返回错误,避免新增音频变体(或上游白名单被
  放宽)时被静默记成音效
2026-09-21 00:05:44 +08:00
suzmii 573f9f447a 补齐 AGC「生成动画」面板的宿主样式并加样式缺口守卫
- resourceCanvasChrome.css 逐条照抄 src/index.css 的角色动画面板规则:面板 / 提示词 textarea / 动作预设 / 底部动作行 / 提交按钮,以及关闭键、素材缩略图与 reference-chip 全族
- 窄屏(≤720px)与网页端同口径:面板落到底部整宽、动作竖排、提交按钮整宽
- 新增 tests/resourceCanvasCharacterAnimationPanelStyle.test.tsx:渲染共享动画面板,按 src/main.tsx 的真实 import 关系解析 AGC 实际加载的样式表,断言「网页端给出规则的类 AGC 必须也有一条」并抽查撑开布局的关键声明
- 变异验证:临时移除本次补的样式块,守卫用例列出缺规则的 15 个类并红灯
2026-09-20 23:48:53 +08:00
lhk229 81e0c41f1c 策划agent可设置模型和推理挡 (#436)
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Reviewed-on: #436
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-09-20 23:46:33 +08:00
k88936 9a6875b4ce Merge remote-tracking branch 'origin/master' 2026-09-20 23:45:36 +08:00
k88936 99fb6c38c1 AGC 音频生成并入图片类那份后台任务账本
- 原生:`start_local_project_asset_generation` 新增可选入参 `idempotencyKey`,并在音频 kind 上分叉;图片类载荷与分支逐字未改
- 原生:新增音频提交期收口 `prepare_local_project_audio_generation`(kind / 提示词上限 / 素材名 / operation 身份 / 幂等键)
- 原生:新增 `run_local_project_audio_generation_at`,在派发时刻读项目 revision 并复用既有音频无源生成实现,不复制生成逻辑
- 原生:新增 `begin_local_project_audio_generation_task` / `run_local_project_audio_generation_task`,音频任务落同一份项目内账本并写 running → completed / failed,跑完没登记素材按失败收口
- 原生:补两条用例——被拒绝的提交零写入账本、音频任务落在同一账本且 kind 正确
- 前端任务模型:新增音频任务与 `idempotencyKey` 字段,恢复出来的历史任务不带它也不承接重试,入口文案扩到音频栏目
- 前端队列:按 kind 分流派发载荷,音频只发任务身份(任务 id 即 operation id)与幂等键
- 前端面板:音频生成面板改为点「生成」同步提交并立即关闭,删除「生成中…」「后台运行并关闭」与输入锁定
- 前端宿主:音频提交改为同步入队并展开「生成任务」侧栏,只有「后端从未受理」才连原草稿与原请求身份重开面板
- 前端清理:删除随本次改动失效的 `resourceCanvasGenerationSourceId` 与宿主里不再使用的 import
- 测试:面板 / 队列 / 宿主生命周期 / 落点 / appSurface 改按后台账本口径断言,并补「未受理即时失败重开」用例(已用移除重开逻辑的变异验证其非空)
- 文档:PRD §3.10 / §7.9、AGC 底部工具栏入口矩阵、V3 端到端验收 S11a 同步为音频后台化口径
- 文档:新增里程碑与实施计划(含验收证据矩阵),并在 decision-log 记下「音频并入后台任务账本」这条长期约定
2026-09-20 23:32:09 +08:00
suzmii 5b73082ad0 补齐画布验收第二批:派生任务进生成任务侧栏、提交中让出草稿与重复提交防护
- 新增 resourceCanvasResourceEditTaskModel:原生资源编辑阶段到面板状态/文案的映射,本地提交记录与原生待办按 operationId 合并,派生任务的终态与耗时判据
- 「生成任务」侧栏同时收派生/修改类任务:与图片类生成任务同一条列表、同一套分栏与状态徽标,多一行提示词,样式沿用既有卡片类
- 快速编辑、生成动画、视频/音效/背景音乐三条派生提交:按下提交当帧写入本地记录并在侧栏可见,成功后收口为已完成、失败带原因并重读原生待办
- 「管理未完成编辑」不再列出正在提交的那一笔草稿,失败后自动回落,提示词与 @ 引用不丢
- 同一张素材存在在途提交时,重新打开面板再提交会被拦下并给出原因,避免重复派生与重复计费
- 侧栏与工具条入口的在途计数把派生任务一并计入
- 新增 tests/resourceCanvasResourceEditTasks.test.ts(9 条);resourceCanvasQuickEditDraft.test.tsx 补两组宿主用例(提交当帧进侧栏并收口、在途重复提交被拦),两处新判据均做过变异验证
- 同步 PRD 派生任务侧栏口径、decision-log 与画布验收待办文档
2026-09-20 23:04:13 +08:00
suzmii d354e5e7c3 修复发号Job未授权调度器读取归档导致整轮调度失败
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 20s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 20s
Project CI / Native shell tests (pull_request) Failing after 19s
Project CI / Backend tests (pull_request) Failing after 20s
Project CI / Frontend tests (pull_request) Failing after 9s
Project CI / Repository checks (pull_request) Failing after 13s
Project CI / AI game creator shell web tests (pull_request) Failing after 13s
发号Jenkinsfile显式copyArtifactPermission授权调度管线
生产门禁新增该授权规则避免回归
运维文档补充生产者到调度器的Copy Artifact映射与失败后果
2026-09-20 22:58:39 +08:00
suzmii 62bffebf8e Merge remote-tracking branch 'origin/master' into feat/jenkins-mac-build
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 20s
Project CI / Backend tests (pull_request) Failing after 20s
Project CI / Native shell tests (pull_request) Failing after 20s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 21s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 21s
Project CI / Frontend tests (pull_request) Failing after 7s
Project CI / AI game creator shell web tests (pull_request) Failing after 11s
Project CI / Repository checks (pull_request) Failing after 11s
# Conflicts:
#	docs/project-memory/shared-memory/pitfalls.md
2026-09-20 22:54:29 +08:00
suzmii dbbbef8a0d 把macOS渠道纳入定时发布并共用总版本号
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 17s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 16s
Project CI / Native shell tests (pull_request) Failing after 16s
Project CI / Backend tests (pull_request) Failing after 16s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 16s
Project CI / Frontend tests (pull_request) Failing after 7s
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / AI game creator shell web tests (pull_request) Failing after 11s
调度器在发号后同时触发macOS渠道构建,透传同一个总版本号与固定revision
macOS Job改为默认真实发布,演练需显式勾选
新增SKIP_IF_SUPERSEDED:节点离线期间排队的旧构建在恢复后自行让位
补充生产门禁规则与回归断言
同步调度与运维文档口径
2026-09-20 22:47:01 +08:00
kdletters fdc48aa573 将 Godot 原生引导迁移到官方 C++ 绑定
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 17s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 18s
Project CI / Backend tests (push) Failing after 17s
Project CI / AI game creator shell Rust smoke (push) Failing after 19s
Project CI / Native shell tests (push) Failing after 18s
Project CI / AI game creator shell Rust crates (push) Failing after 18s
Project CI / Frontend tests (push) Failing after 6s
Project CI / AI game creator shell web tests (push) Failing after 11s
Project CI / Repository checks (push) Failing after 11s
使用固定版本 godot-cpp 替换手写 C ABI 引导并保留执行协议
采用 MSVC 与 CMake 构建,校验依赖归档和源码缓存
修复动态卸载时的实例绑定与单例包装生命周期
补充构建缓存测试、实机验收结果及分发文档
2026-09-20 22:00:44 +08:00
suzmii 88090fcc6e Merge remote-tracking branch 'origin/master' into feat/jenkins-mac-build
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 17s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 17s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 17s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 17s
Project CI / Backend tests (pull_request) Failing after 19s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 20s
Project CI / Native shell tests (pull_request) Failing after 19s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 19s
Project CI / Frontend tests (pull_request) Failing after 6s
Project CI / AI game creator shell web tests (pull_request) Failing after 12s
Project CI / Repository checks (pull_request) Failing after 12s
# Conflicts:
#	docs/project-memory/shared-memory/pitfalls.md
2026-09-20 21:34:08 +08:00
suzmii a0d432b63d Merge remote-tracking branch 'origin/master' into fix/agc-acceptance-followup
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 19s
Project CI / Native shell tests (pull_request) Failing after 18s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 19s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 19s
Project CI / Backend tests (pull_request) Failing after 19s
Project CI / Frontend tests (pull_request) Failing after 6s
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / AI game creator shell web tests (pull_request) Failing after 12s
2026-09-20 21:27:23 +08:00
suzmii d4de81c128 记录Mac Job与节点描述的维护口径
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 17s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 18s
Project CI / Backend tests (pull_request) Failing after 18s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 18s
Project CI / Native shell tests (pull_request) Failing after 17s
Project CI / Frontend tests (pull_request) Failing after 7s
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / AI game creator shell web tests (pull_request) Failing after 12s
补充Job描述与节点描述属Jenkins侧元数据、不随仓库同步的说明
记录该Job的config.xml POST返回500、须用脚本接口原地改描述以保留历史
同步并行度默认值与描述口径
2026-09-20 21:19:23 +08:00
suzmii c0154c2aed 优化Mac构建管线并行度与入口健壮性
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 15s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 12s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 15s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 18s
Project CI / Frontend tests (pull_request) Failing after 8s
Project CI / Repository checks (pull_request) Failing after 16s
Project CI / Native shell tests (pull_request) Failing after 16s
Project CI / Backend tests (pull_request) Failing after 16s
Project CI / AI game creator shell web tests (pull_request) Failing after 12s
并行度参数默认改为8以吃满节点,并在说明里记录其作为rustc jobserver令牌上限的影响
产品名统一从Tauri配置推导,校验脚本从Info.plist读取可执行名,改产品名不再静默失效
隔离smoke改用ditto --clone复制副本,实测整轮6.8秒
Agent工作区守卫改为按目录约定匹配,节点改名后仍成立
签到同时取master以便解析上次发布commit,失败仅降级更新摘要
补充上述行为的回归断言
2026-09-20 21:00:05 +08:00
kdletters 429f991bde 回滚误入 master 的游戏分发 WIP(夹带在 OSS v1 签名提交里)
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 19s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 20s
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 20s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 20s
Project CI / Backend tests (push) Failing after 17s
Project CI / AI game creator shell Rust crates (push) Failing after 17s
Project CI / AI game creator shell Rust smoke (push) Failing after 19s
Project CI / Native shell tests (push) Failing after 18s
Project CI / Frontend tests (push) Failing after 8s
Project CI / AI game creator shell web tests (push) Failing after 15s
Project CI / Repository checks (push) Failing after 17s
- 撤销 39aed2e48 夹带的游戏分发前后端、SpacetimeDB 绑定、主站路由、后台审核页、文档与 nginx 改动,master 恢复可编译状态
- 保留该提交自身的 AGC 发号改动:agc-global-version.mjs 与其参数构造单测
- 恢复被误删的 output 产物,避免把无关清理混进该提交
- 补回两篇技术文档在 docs/README.md 的索引(它们属于后续两个补提交文档,不属于游戏分发)
- 游戏分发完整实现保留在 codex/game-distribution 分支继续开发
2026-09-20 20:52:24 +08:00
suzmii c4b391cb84 记录Mac构建管线首次端到端验收结果
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 17s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 18s
Project CI / Native shell tests (pull_request) Failing after 17s
Project CI / Backend tests (pull_request) Failing after 17s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 18s
Project CI / Frontend tests (pull_request) Failing after 7s
Project CI / AI game creator shell web tests (pull_request) Failing after 11s
Project CI / Repository checks (pull_request) Failing after 12s
回填build3成功的产物、验签、dry-run与耗时证据
补充--no-sign连带跳过更新包签名、残留产物不幂等两处踩坑
记录主crate重复编译这一剩余耗时优化点
2026-09-20 20:44:12 +08:00
suzmii bd0f9f481a 修复Mac构建产物不幂等导致的DMG重建失败
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 17s
Project CI / Backend tests (pull_request) Failing after 16s
Project CI / Native shell tests (pull_request) Failing after 16s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 17s
Project CI / Frontend tests (pull_request) Failing after 6s
Project CI / AI game creator shell web tests (pull_request) Failing after 12s
Project CI / Repository checks (pull_request) Failing after 12s
构建前删除本次将写出的DMG、更新包与签名,避免同名文件让hdiutil失败
hdiutil create显式覆盖,杜绝残留对象影响本次产物
补充回归断言,防止旧签名让验签门禁误通过
2026-09-20 19:58:30 +08:00
lhk229 a2acfa9a6f 修复 Godot 原生插件的 MSVC 编译兼容性
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 17s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 17s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 16s
Project CI / Native shell tests (pull_request) Failing after 15s
Project CI / Backend tests (pull_request) Failing after 16s
Project CI / Frontend tests (pull_request) Failing after 6s
Project CI / AI game creator shell web tests (pull_request) Failing after 12s
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 19s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 19s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 19s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 19s
Project CI / AI game creator shell Rust crates (push) Failing after 20s
Project CI / AI game creator shell Rust smoke (push) Failing after 20s
Project CI / Backend tests (push) Failing after 20s
Project CI / Native shell tests (push) Failing after 19s
Project CI / Frontend tests (push) Failing after 6s
Project CI / AI game creator shell web tests (push) Failing after 12s
Project CI / Repository checks (push) Failing after 12s
将 ABI 存储改为 C11 显式 16 字节对齐并保留 128 字节容量
补充 Windows 编译环境要求与 MSVC 对齐问题排障说明
2026-09-20 19:39:12 +08:00
suzmii 6fb17de9db Merge remote-tracking branch 'origin/master' into fix/agc-acceptance-followup
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 19s
Project CI / Native shell tests (pull_request) Failing after 19s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 19s
Project CI / Backend tests (pull_request) Failing after 19s
Project CI / Frontend tests (pull_request) Failing after 7s
Project CI / AI game creator shell web tests (pull_request) Failing after 14s
Project CI / Repository checks (pull_request) Failing after 15s
2026-09-20 19:25:45 +08:00
suzmii 57017ee046 修复Mac入口误用--no-sign导致更新包无签名
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 18s
Project CI / Native shell tests (pull_request) Failing after 18s
Project CI / Backend tests (pull_request) Failing after 18s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 19s
Project CI / Frontend tests (pull_request) Failing after 6s
Project CI / AI game creator shell web tests (pull_request) Failing after 12s
Project CI / Repository checks (pull_request) Failing after 12s
移除build-macos-ci的--no-sign:该标志会连带跳过updater的minisign签名
改为剥离APPLE_*凭据跳过Apple签名,保持更新包签名能力
构建清单按codesign实测记录appleSigned与签名类型
补充回归断言与运维、更新规范口径
2026-09-20 19:11:30 +08:00
kdletters cde34428f9 补提交 AGC 后端框架与演进路线文档
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 18s
Project CI / AI game creator shell Rust smoke (push) Failing after 18s
Project CI / AI game creator shell Rust crates (push) Failing after 19s
Project CI / Native shell tests (push) Failing after 19s
Project CI / Backend tests (push) Failing after 19s
Project CI / Frontend tests (push) Failing after 7s
Project CI / Repository checks (push) Failing after 11s
Project CI / AI game creator shell web tests (push) Failing after 12s
docs/README.md 已引用但文件未入库,是文档索引门禁失败的第二处缺失\n补齐后 check:doc-index 可在干净检出上通过
2026-09-20 18:53:20 +08:00
kdletters 07dc1e9add 补提交 Responses API 与 Agents SDK 迁移评估文档
docs/README.md 已引用该评估文档但文件未入库,导致 check:doc-index 与 pre-push 对所有人生效失败
本次把文档正文补进仓库,恢复文档索引门禁
2026-09-20 18:52:59 +08:00
suzmii bdf6d0f556 Mac构建并行度改为可调参数并记录首次缓存表现
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 18s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 18s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 19s
Project CI / Backend tests (pull_request) Failing after 18s
Project CI / Native shell tests (pull_request) Failing after 18s
Project CI / Frontend tests (pull_request) Failing after 7s
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / AI game creator shell web tests (pull_request) Failing after 11s
CARGO_BUILD_JOBS从写死4改为Job参数默认6,适配8核共用机器
补充并行度与sccache首次全未命中的运维口径
增加Jenkinsfile并行度参数的回归断言
2026-09-20 18:51:36 +08:00
kdletters 39aed2e486 OSS 写入显式使用 v1 签名
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 19s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 19s
Project CI / AI game creator shell Rust crates (push) Failing after 18s
Project CI / Native shell tests (push) Failing after 18s
Project CI / Backend tests (push) Failing after 18s
Project CI / AI game creator shell Rust smoke (push) Failing after 19s
Project CI / Frontend tests (push) Failing after 7s
Project CI / AI game creator shell web tests (push) Failing after 11s
Project CI / Repository checks (push) Failing after 11s
ossutil v2 默认 v4 签名,缺 region 会直接失败\n总号写入与回读统一走 buildOssutilArgs,默认 --sign-version v1,可切 v4 并配合 AGC_OSS_REGION\n补充参数构造单测
2026-09-20 18:33:02 +08:00
kdletters 752116fe41 改用可执行性校验确认 ossutil 就绪
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 19s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 19s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 20s
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 20s
Project CI / AI game creator shell Rust smoke (push) Failing after 18s
Project CI / Native shell tests (push) Failing after 18s
Project CI / Backend tests (push) Failing after 18s
Project CI / AI game creator shell Rust crates (push) Failing after 19s
Project CI / Frontend tests (push) Failing after 7s
Project CI / AI game creator shell web tests (push) Failing after 11s
Project CI / Repository checks (push) Failing after 11s
ossutil v2 不支持 --version,版本打印会误判为失败
引导阶段只校验二进制存在且可执行,具体命令错误仍由发号步骤暴露
2026-09-20 18:24:42 +08:00
kdletters d64ce64056 修复 AGC 发号 Job 的 ossutil 引导路径
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 19s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 19s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 19s
Project CI / Native shell tests (push) Failing after 19s
Project CI / AI game creator shell Rust crates (push) Failing after 19s
Project CI / Backend tests (push) Failing after 19s
Project CI / AI game creator shell Rust smoke (push) Failing after 19s
Project CI / Frontend tests (push) Failing after 6s
Project CI / AI game creator shell web tests (push) Failing after 11s
Project CI / Repository checks (push) Failing after 12s
官方 ossutil v2 zip 解压后带版本目录,先定位二进制再落到工作区固定路径
避免 chmod 找不到文件导致发号 Job 在引导阶段失败
2026-09-20 18:20:58 +08:00
kdletters 9e0cbc7041 AGC 客户端版本号收敛为单一发号源
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 18s
Project CI / Backend tests (push) Failing after 18s
Project CI / Native shell tests (push) Failing after 18s
Project CI / AI game creator shell Rust crates (push) Failing after 18s
Project CI / AI game creator shell Rust smoke (push) Failing after 19s
Project CI / Frontend tests (push) Failing after 7s
Project CI / AI game creator shell web tests (push) Failing after 12s
Project CI / Repository checks (push) Failing after 12s
新增 OSS agc/global-version.json 发号模块与发号入口脚本\n构建侧优先采用发号 Job 下发的总号,未传入时本地兜底发号\n渠道高水位降级为回退断言,请求号低于渠道清单版本即失败关闭\n新增 Genarrative-Agc-Global-Version-Issue 发号 Job 与 job config\n调度管线与手动管线先发号再透传 AGC_RELEASE_VERSION\n补充单元测试、生产运维门禁、主规范与里程碑文档
2026-09-20 18:13:45 +08:00
lhk229 4864e92e30 修复策划智能体提示词边界与资源引用 (#433)
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 18s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 18s
Project CI / AI game creator shell Rust smoke (push) Failing after 19s
Project CI / Native shell tests (push) Failing after 19s
Project CI / Backend tests (push) Failing after 19s
Project CI / AI game creator shell Rust crates (push) Failing after 19s
Project CI / Frontend tests (push) Failing after 6s
Project CI / AI game creator shell web tests (push) Failing after 12s
Project CI / Repository checks (push) Failing after 12s
恢复顾问态不自主推进、不安排下一步和不提交阶段审批的约束
恢复过程文档轻量记录原则并明确审批与问询的职责边界
补回顶层设计的排除方向及星露谷示例定位
统一星露谷分析示例的五处旧路径引用
同步策划专题文档与团队共享约定

Reviewed-on: #433
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-09-20 18:07:37 +08:00
kdletters 288e1d4f4a 同步最新主干并修复 Godot 插件启动竞态
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 13s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 12s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 12s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 13s
Project CI / AI game creator shell Rust smoke (push) Failing after 12s
Project CI / AI game creator shell Rust crates (push) Failing after 20s
Project CI / Backend tests (push) Failing after 23s
Project CI / Native shell tests (push) Failing after 12s
Project CI / Frontend tests (push) Failing after 5s
Project CI / Repository checks (push) Failing after 11s
Project CI / AI game creator shell web tests (push) Failing after 13s
合入主干 Rust 1.98.1 工具链及对应 CI 文档更新
Godot 插件先接收受控项目快照再注册命令和连接能力
补充启动竞态回归、失败回执诊断及插件启动合同
2026-09-20 17:46:53 +08:00
suzmii bb1df3c6da Merge remote-tracking branch 'origin/master' into feat/jenkins-mac-build
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 11s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 11s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 11s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 12s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 12s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 11s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / Native shell tests (pull_request) Failing after 14s
Project CI / Frontend tests (pull_request) Failing after 6s
Project CI / Repository checks (pull_request) Failing after 13s
Project CI / AI game creator shell web tests (pull_request) Failing after 12s
# Conflicts:
#	docs/technical/【技术方案】AGC客户端更新检查与下载-2026-08-31.md
2026-09-20 17:45:38 +08:00
kdletters 5822b64d7c 合并 Godot 编辑器插件与常用操作指导到主分支
接入 Godot 原生桥、受控执行、Runner 回执与编辑器操作指南
保留主分支 Cocos 和 Unity 跨工程能力及外置提示词结构
解决插件生命周期、工具目录、前端启动和文档合并冲突
2026-09-20 17:38:48 +08:00
suzmii 1e17d2c852 补齐画布验收第一批:润色回包提示、未提交草稿恢复与派生名预检
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 7m44s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 7m57s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 5m44s
Project CI / Backend tests (pull_request) Failing after 16s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 5m53s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m21s
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m27s
Project CI / Frontend tests (pull_request) Successful in 7m4s
Project CI / AI game creator shell web tests (pull_request) Successful in 6m22s
Project CI / Native shell tests (pull_request) Successful in 10m13s
AI 润色回包与原文相同时给出明确提示,不再落下点了等于没点的「恢复原文」假入口
润色「与原文相同」的判据改为规范化之后要写回宿主的文本,回包被长度上限截回原文同样算没变化
共享聊天输入区显示润色提示,并让提示随用户手改提示词失效
快速编辑未提交草稿改按资源路径归属,收起面板或被旁路重投影后都不再静默丢掉
恢复入口按路径与本会话草稿对号,并区分「本会话未提交」与原生账本条目
账本读取失败时入口仍报出本会话草稿条数,「继续编辑」重开面板回填提示词与 @ 引用
派生资源名预检接入快速编辑与角色动画两条提交路径,早于正规化写盘
名称预检自身抛错时回写面板失败态,不再留下「点了没反应」的提交按钮
浮层关闭判定改在捕获阶段冻结,避免浮层内按钮卸载自己时整块面板被自己人收掉
恢复入口动作按钮收进统一容器,补移动端纵向排布与禁用态样式
空草稿表改工厂函数,跨语言对表用例按模块路径解析 Rust 源码
补回归用例:旁路重投影后的草稿、原样回包提示、提交前名称拦截、草稿表读写与跨卡隔离
同步 decision-log 与画布验收待办的进展说明
2026-09-20 17:36:02 +08:00
suzmii e230d9dc32 Merge pull request '升级 Rust toolchain 至 1.98.1' (#432) from chore/rust-toolchain-1-98 into master
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 12s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 11s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 11s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 13s
Project CI / AI game creator shell Rust smoke (push) Failing after 20s
Project CI / AI game creator shell Rust crates (push) Failing after 28s
Project CI / Backend tests (push) Failing after 26s
Project CI / Frontend tests (push) Failing after 9s
Project CI / Native shell tests (push) Failing after 20s
Project CI / Repository checks (push) Failing after 22s
Project CI / AI game creator shell web tests (push) Failing after 25s
Reviewed-on: #432
2026-09-20 17:35:17 +08:00
suzmii b10a2d5d79 Merge branch 'master' into chore/rust-toolchain-1-98
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (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
2026-09-20 17:35:09 +08:00
kdletters b96473836d 补齐 Unity 与 Godot 常用操作指导
新增两种编辑器的内置 Skill 和场景、资源、UI、保存撤销示例
接入 DirectProject 按需读取与 Runtime 同源操作参考
补齐指南原文实机测试、安装投影及工具说明完整性检查
记录 Godot 图形补验结果并保留尚未验收的边界
2026-09-20 17:26:34 +08:00
suzmii a35cdcc68c 同步 CI 镜像 tag 与文档口径,并补上工具链一致性护栏
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 17s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 11s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 14s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 12s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 11s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 10s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Native shell tests (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Failing after 5s
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / AI game creator shell web tests (pull_request) Failing after 11s
- 将 CI job 镜像默认 tag 从 genarrative/gitea-project-ci:20260807.1 升为 20260920.1
- 将 Dockerfile 两处 org.opencontainers.image.version 统一为 2026.09.20.1(原先一处 2026.07.23.1、一处 2026.08.07.1,后写的覆盖先写的)
- 同步 deploy/container/README.md 与开发运维文档中的镜像归档示例名
- 订正 pitfalls.md 中 CI 镜像的 Rust 版本,并记录 number_of_links 在 1.98.1 上实测仍未稳定
- 在 decision-log.md 追加本次 Rust 工具链升级到 1.98.1 的决策条目
- 在 scripts/project-ci-workflow.test.ts 新增一致性用例:镜像版本段、digest 与 tag 必须与 rust-toolchain.toml 及两份运维文档一致
2026-09-20 17:06:49 +08:00
kdletters 37189c9252 记录项目主模型使用历史并补录旧项目
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 7m38s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 7m40s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 7m45s
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 8m27s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m24s
Project CI / AI game creator shell Rust crates (push) Successful in 3m8s
Project CI / Repository checks (push) Successful in 7m53s
Project CI / Frontend tests (push) Successful in 8m48s
Project CI / Native shell tests (push) Successful in 11m14s
Project CI / Backend tests (push) Successful in 12m54s
Project CI / AI game creator shell web tests (push) Successful in 5m53s
在项目 .agent 中保存请求模型与响应确认型号,区分目录标识和真实模型
为旧项目补录带来源标记的模型信息,保留历史并确保重复打开幂等
在 Direct Codex 代理中提取模型元数据,异步落盘且不阻塞响应转发
补充模型追溯边界测试并同步技术方案与团队约定
2026-09-20 17:02:44 +08:00
kdletters 54d0fb75ea 接入 Godot 编辑器插件与受控执行链路
新增 GDExtension 自动引导、GDScript 执行和实例隔离缓存
接入 AGC 插件开关、Runner、Agent 工具与权限审计
完善执行回执确认、不确定状态阻断及卸载恢复
补齐 Windows 分发资源、定向测试与实机验收文档
2026-09-20 16:35:04 +08:00
suzmii e1c8edd6bf Merge remote-tracking branch 'origin/master' into fix/rust-toolchain-1-98
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 19s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 17s
Project CI / Backend tests (pull_request) Failing after 18s
Project CI / Native shell tests (pull_request) Failing after 18s
Project CI / AI game creator shell Rust crates (pull_request) Failing after 18s
Project CI / Frontend tests (pull_request) Failing after 7s
Project CI / AI game creator shell web tests (pull_request) Failing after 11s
Project CI / Repository checks (pull_request) Failing after 12s
2026-09-20 15:59:17 +08:00
suzmii 8a3daff55b 升级 Rust 工具链到 1.98.1
- 将 rust-toolchain.toml 的 channel 从 1.96.0 提升到 1.98.1,保留 rustfmt 组件要求
- 将 CI job 镜像 Rust stage 从 rust:1.96-bookworm 更新为 rust:1.98-bookworm,并同步新的固定镜像 digest
- 同步 deploy/container/README.md 中 Rust 版本与 digest 的说明
- 同步开发运维文档中 CI 环境与 rust-toolchain.toml 的版本口径为 1.98.1
2026-09-20 15:59:07 +08:00
kdletters 97d8a6c39a 清理并统一外置智能体提示词
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 7m34s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 7m42s
Project CI / AI game creator shell Rust shard 3/4 (push) Failing after 7m43s
Project CI / AI game creator shell Rust shard 1/4 (push) Failing after 8m1s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m58s
Project CI / AI game creator shell Rust crates (push) Successful in 3m9s
Project CI / Backend tests (push) Successful in 12m4s
Project CI / Frontend tests (push) Successful in 11m37s
Project CI / Native shell tests (push) Successful in 15m37s
Project CI / Repository checks (push) Successful in 13m51s
Project CI / AI game creator shell web tests (push) Successful in 6m52s
将智能体系统指令、工具说明和恢复模板集中到外置提示词目录
精简重复否定、历史对照与无关实现说明,保留必要执行约束
扩展提示词构建校验和源码边界检查并接入现有本地与持续集成门禁
同步内置技能、协作规范、技术文档与对应验证断言
2026-09-20 15:32:48 +08:00
lhk229 70271b408e 删除策划agent v2,与退役功能解耦 (#355)
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m19s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m35s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 7m34s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m39s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m13s
Project CI / AI game creator shell Rust crates (push) Successful in 3m18s
Project CI / Backend tests (push) Successful in 9m16s
Project CI / Repository checks (push) Successful in 8m11s
Project CI / Native shell tests (push) Successful in 11m56s
Project CI / Frontend tests (push) Successful in 11m49s
Project CI / AI game creator shell web tests (push) Successful in 6m37s
Reviewed-on: #355
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-09-20 14:49:57 +08:00
kdletters 623e007fae 完善客户端发布渠道并接入模板库灰度
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m36s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m37s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 7m42s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m51s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m55s
Project CI / AI game creator shell Rust crates (push) Successful in 4m15s
Project CI / Repository checks (push) Successful in 4m45s
Project CI / Frontend tests (push) Successful in 7m47s
Project CI / Native shell tests (push) Successful in 9m33s
Project CI / Backend tests (push) Successful in 10m9s
Project CI / AI game creator shell web tests (push) Successful in 4m38s
区分发布渠道与系统,支持 dev、release 和自定义渠道
允许网站通过服务端配置选择客户端下载检测渠道
接入模板库灰度权限并阻断退出和切号后的异步操作
补齐发布、下载、灰度与会话竞态测试及当前规范
2026-09-20 12:12:49 +08:00
kdletters fc0ce4ee5f 补齐跨工程插件启动的界面测试响应
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m17s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m29s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 7m36s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m42s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m28s
Project CI / AI game creator shell Rust crates (push) Successful in 2m47s
Project CI / Backend tests (push) Successful in 8m49s
Project CI / Repository checks (push) Successful in 8m40s
Project CI / Frontend tests (push) Successful in 9m41s
Project CI / Native shell tests (push) Successful in 12m29s
Project CI / AI game creator shell web tests (push) Failing after 4m51s
为五个项目打开与对话恢复测试提供插件项目绑定及空插件列表响应

保留原有业务状态断言和产品错误提示,修复完整界面门禁暴露的测试桩缺口
2026-09-20 10:58:10 +08:00
kdletters c1d26f11df 移除 Cocos 和 Unity 插件的工程类型限制
统一插件列表、启动、面板和 Agent 工具的可用性判断,保留开关与平台约束

调整前端自动启动并修复 Cocos 项目切换的旧连接与订阅快照竞态

补充插件宿主、工具目录和前端回归测试,同步插件技术规范与共享决策
2026-09-20 10:53:16 +08:00
kdletters 15774a1c02 修复项目自动上传并增加后台完整工程下载
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m24s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 7m25s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m28s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m31s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m57s
Project CI / AI game creator shell Rust crates (push) Successful in 2m41s
Project CI / Repository checks (push) Successful in 4m10s
Project CI / Native shell tests (push) Successful in 11m20s
Project CI / Frontend tests (push) Successful in 10m30s
Project CI / AI game creator shell web tests (push) Successful in 5m41s
Project CI / Backend tests (push) Successful in 14m9s
接入真实项目打开、切换和关闭生命周期并修复退出等待竞态
新增后台项目列表及按原目录校验导出的 ZIP 下载
补充快照完整性元数据、后台权限和取消清理
固定默认 AGC 存储目标并支持空文件与特殊字符路径
补充自动化测试及真实 OSS 只读导出验证
记录本地数据库 404 导致完整 HTTP 联调和安装版实机验证待补
2026-09-20 10:07:31 +08:00
suzmii b1cadd0cc8 Mac 渠道接入更新包签名、OSS 上传与渠道清单
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 7m17s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 7m25s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 7m26s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 7m34s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m9s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m17s
Project CI / Backend tests (pull_request) Successful in 11m30s
Project CI / Repository checks (pull_request) Successful in 11m36s
Project CI / Frontend tests (pull_request) Successful in 13m46s
Project CI / Native shell tests (pull_request) Successful in 16m19s
Project CI / AI game creator shell web tests (pull_request) Successful in 5m31s
问题:mac 侧此前只有 archive-only 构建,dev-mac 渠道不产出更新包、不上传、无渠道
清单,客户端拿不到 macOS 更新(里程碑曾因签名/公证凭据未就绪而暂缓)。

改动:
- build-macos-ci.mjs 改为 dev-mac 发布入口:开启 createUpdaterArtifacts 产出
  .app.tar.gz + .sig,生成 universal DMG 与渠道清单 latest.json,构建后用产物内
  烘焙的公钥复核签名,再按 AGC_RELEASE_DRY_RUN 决定是否上传 OSS
- 新增 verify-updater-signature.mjs(minisign ED 预哈希校验)+ 单测:验签失败或
  keyId 不一致立即失败关闭,绝不写 OSS
- Jenkinsfile:新增 AGC_RELEASE_VERSION / AGC_RELEASE_DRY_RUN(默认开启)/
  AGC_UPDATE_RELEASE_NOTES / OSSUTIL_BIN 参数;withCredentials 注入
  AgcUpdaterSigningKey(+密码) 与 AliyunAccessKeyId/Secret;归档补 latest.json、
  .sig 与更新摘要
- 既有单测口径更新:archive-only → 发布型,并新增「必须先验签再上传」守卫
- 文档:技术方案、里程碑、运维文档同步;Apple 签发与公证暂缺显式记录为
  未验证项(--no-sign、appleSigned=false、notarized=false)

验证:单测 11/11 + 42/42;Jenkinsfile 四个 sh 块语法通过;伪造 bundle 端到端
验证清单(两平台同 URL/同签名、universal 首装包被正确选中)、真实 Tauri 签名
验签通过、错钥匙报 keyId 不一致、篡改报校验失败、dry-run 上传计划顺序正确。
2026-09-19 17:49:25 +08:00
suzmii 261228ed3f 修复 universal 首装 DMG 选择以兼容主线新增校验
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 7m38s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 7m46s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 7m49s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 7m57s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m59s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m20s
Project CI / Repository checks (pull_request) Successful in 7m8s
Project CI / Native shell tests (pull_request) Successful in 13m34s
Project CI / Frontend tests (pull_request) Successful in 12m58s
Project CI / Backend tests (pull_request) Successful in 14m59s
Project CI / AI game creator shell web tests (pull_request) Successful in 5m8s
合并 origin/master 后,e3682fd06 新增的 selectFirstInstallArtifact 对
universal-apple-darwin 会推导出架构 x64,匹配不到本管线产出的
陶泥儿_<version>_universal.dmg,createUpdateManifest 直接抛
「首装 DMG 必须唯一匹配本次版本 x 和架构 x64,找到 0 个」。

- build-release.mjs:为 universal-apple-darwin 增加分支,按
  _<version>_universal.dmg 唯一匹配(与 build-macos-ci.mjs 的命名一致)
- build-release.test.mjs:createDmgFixture 支持 universal 命名;universal 用例
  补首装包断言,锁定两个平台键共用同一个 DMG

验证:管线同款 node --test 33/33 通过(合并前 32/33);prepare-macos-codex
与 release-oss 用例同跑通过。
2026-09-19 17:03:18 +08:00
suzmii 8c17e40d7d 合并 origin/master 并解决 pitfalls.md 冲突
- 冲突仅 1 处:踩坑文档顶部双方各新增条目(本次合并相对 master 落后 88 个提交)
- 解决方式:保留 master 的 2026-09-19 / 2026-09-17 两条在前,分支的
  「universal 主程序必须配套双架构原生依赖」在后;另补齐标题前缺失的空行
- 本次提交内容为上游已合入 master 的文件,跳过 lint-staged/Husky,
  避免对 master 侧 99 个 js/ts 文件重新格式化产生无意义改动
2026-09-19 17:01:57 +08:00
suzmii 237e440057 Mac Jenkins节点接入sccache编译缓存
原因:macOS universal 构建此前在 Jenkinsfile 与 build-macos-ci.mjs 两处把
RUSTC_WRAPPER 清空,依赖 crate 每次从零编译且双架构各编译一遍,逼近 90 分钟超时。

改动:
- Jenkinsfile 对齐 Linux 生产管线:RUSTC_WRAPPER=sccache、CARGO_INCREMENTAL=0、
  SCCACHE_CACHE_SIZE=20G,SCCACHE_DIR 固定到稳定缓存根
  ${HOME}/caches/genarrative-jenkins/agc-macos/sccache(可用
  GENARRATIVE_AGC_MACOS_CACHE_ROOT 覆盖),并输出 sccache --show-stats 供判断命中率。
- build-macos-ci.mjs 不再清空 RUSTC_WRAPPER,wrapper 由管线显式声明。
- 保留 CARGO_TARGET_DIR 指向 workspace 内 target,跨构建继续复用增量产物。
- 文档补充缓存根、覆盖变量与回退语义。

验证:sh -n/bash -n 通过;管线同款 node --test 34 项全通过;本地探针证明空缓存
首次 miss 编译、清空 target 后再次构建命中缓存不再编译。
2026-09-19 16:55:00 +08:00
kdletters e3682fd06f 固定客户端dev服务并支持官网多平台下载
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 7m31s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m32s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m33s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m40s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m21s
Project CI / AI game creator shell Rust crates (push) Successful in 3m11s
Project CI / Native shell tests (push) Successful in 16m15s
Project CI / Frontend tests (push) Successful in 14m57s
Project CI / Repository checks (push) Successful in 14m48s
Project CI / Backend tests (push) Successful in 20m7s
Project CI / AI game creator shell web tests (push) Successful in 6m20s
移除服务器选择并按来源隔离登录凭据
新增官网客户端下载入口和匿名平台聚合接口
根据发布清单自动展示Windows与macOS首装包
补齐Mac首装元数据和上传顺序校验
同步定向测试与下载发布规范
2026-09-19 16:19:28 +08:00
kdletters 33f5ad68bf 接入 DotCraft Unity 编辑器插件与受控执行链路 (#423)
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m19s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m26s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 7m32s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m34s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m57s
Project CI / AI game creator shell Rust crates (push) Successful in 3m18s
Project CI / Native shell tests (push) Successful in 10m54s
Project CI / Backend tests (push) Successful in 12m42s
Project CI / Frontend tests (push) Successful in 13m4s
Project CI / AI game creator shell web tests (push) Successful in 5m1s
Project CI / Repository checks (push) Successful in 12m48s
AGC 原有插件系统无法直接操作已打开的 Unity Editor。本变更增加内置 `agc-unity-editor`,在 Windows x64 / Unity Mono 上支持当前项目探测、连接与 C# 执行,不向 Unity 工程安装 UPM 桥接包。

## 主要变更

- 固定复用 DotCraft.Unity 0.4.3 的 Attach 核心,提供自包含 .NET helper,保留上游许可证、来源及修改记录。
- GUI、Runtime、DirectProject 共用 Runner 执行服务;补齐项目身份、并发、总期限、回执确认与持久不确定状态阻断。
- 现有打开项目入口支持 Unity,按项目类型及开关暴露插件和 Agent 工具。
- Windows 构建准备 helper 并随包分发;插件 JS/Rust 测试接入现有 CI 组,Jenkins 增加 .NET 10 工具链预检。

## 验证

- .NET helper 27 项测试、自包含发布及最小环境协议 smoke 通过。
- Unity 6000.3.7f1 实机验证通过:连接、C# 执行、编译错误修复、断连重连、Domain Reload 后重新握手;真实 Runner 的 ACK、并发拒绝和跨重启阻断通过。
- 宿主 Unity、PluginHost、Cocos、MCP、工具目录与引擎识别定向回归通过;前端类型检查、插件 JS/Rust、CI 配置、格式、编码和文档门禁通过。

Linux CI 不代替 Windows helper/实机验证;发行安装包 UI smoke、其它 Unity 版本和 Unity CoreCLR 未验证。Unity 演示工程中的场景和组件已撤销,不在此 PR 范围内。

---------

Co-authored-by: kdletters <61648117+kdletters@users.noreply.github.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/423
2026-09-19 12:29:27 +08:00
kdletters 5bf036bb81 收敛后端依赖装配与鉴权并完善异步追踪 (#425)
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
HTTP 请求取消后,在途计数原先无法释放;项目元数据和 External API 鉴权依赖完整 AppState,相同鉴权和追踪配置也散落在多个入口。本次集中装配这些依赖和横切能力,保持现有公开 API、权限、计费、幂等和事务规则。

## 修改

- 91 个受保护路由集中应用鉴权;保留方法级 404/405/HEAD/Allow、公开入口、MCP、精选缓存头及 2/4 MiB 请求限制。
- 七个项目元数据入口改用缓存的 EditorProjectState,External/MCP 鉴权改用 ExternalApiAuthState;生产实现复用 SpacetimeClient,媒体修复维持原有上传和登记顺序。
- RAII 覆盖请求 Future 取消和 panic unwind 的计数清理;正常与降级服务复用 TraceLayer,指标采用 MatchedPath 模板及固定兜底。
- LLM 普通与流式调用增加跳过参数的异步 span,保持父上下文、流式回调、错误与重试行为;补充替代依赖测试并同步锁文件和文档。

## 验证

| 验证面 | 结果 |
| --- | --- |
| platform-llm 完整本地回归 | 161 个单元测试、3 个集成测试通过;1 个真实 Provider 用例按原配置忽略 |
| api-server 完整回归 | 执行时 1075 通过、12 失败、6 忽略;其中 1 个新增公开读取 fixture 断言已修正,14 个路由契约回归随后全部通过;剩余 11 个是下述既有 Windows 失败 |
| 窄依赖、取消与追踪 | 元数据 owner/幂等/revision、鉴权及 MCP 错误传播、取消/panic/流式响应、追踪父子关系与敏感参数省略均通过 |
| 实际本地服务 | 独立 SpacetimeDB 上 102/102 检查通过,两个动态项目 ID 的路由模板及请求 ID 日志核验 3/3 通过 |
| 编译与边界 | api-server cargo check、AGC 锁文件下 platform-llm cargo check、rustfmt、编码、文档索引、DDD 与 diff 检查通过 |
| 合入最新 master 后 | 后端源码及锁文件保持已测内容;再次通过 14 个路由契约测试、3 个 Provider 追踪测试及编码/文档/DDD/diff 检查 |

实际服务检查覆盖 health/ready、两账号登录、项目 CRUD、幂等重复、跨 owner 拒绝、revision 冲突、External/MCP 读取、Key 撤销及 404/405。使用既有 test 环境的本地 Router 拒绝 fixture,未调用真实付费 Provider;自建服务已关闭,原开发实例保留。

## 已知测试限制

API 全量测试尚未全绿:11 个 wallet_refund_outbox 用例在 Windows 的目录同步处失败。其生产文件与变更前内容一致;标准库隔离复现确认 File::open(目录) 返回 OS 5,而普通文件写入、同步及 hard_link 正常。这个已有的目录持久化问题未混入本次重构,也未通过跳过或弱化相关断言掩盖。

---------

Co-authored-by: kdletters <61648117+kdletters@users.noreply.github.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/425
2026-09-19 12:29:19 +08:00
k88936 5383af4cc5 Merge pull request 'refactor/整理现有的string-typed资源类型' (#388) from feat/agc-organize-assetkind into master
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 7m7s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m13s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m19s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 8m1s
Project CI / AI game creator shell Rust crates (push) Successful in 1m17s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m53s
Project CI / Repository checks (push) Successful in 4m49s
Project CI / Frontend tests (push) Successful in 5m47s
Project CI / Backend tests (push) Successful in 7m37s
Project CI / Native shell tests (push) Successful in 8m4s
Project CI / AI game creator shell web tests (push) Successful in 3m3s
Reviewed-on: #388
2026-09-19 03:51:32 +08:00
k88936 47f7cb2bf1 修复 AGC 前端资源 kind 判据与用例的 canonical 残留
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 7m1s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 7m6s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 7m14s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 7m52s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m31s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m50s
Project CI / Repository checks (pull_request) Successful in 4m44s
Project CI / Frontend tests (pull_request) Successful in 6m20s
Project CI / Backend tests (pull_request) Successful in 8m27s
Project CI / Native shell tests (pull_request) Successful in 8m15s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m10s
- resource-canvas/resourceCanvasAssetGenerationReferenceModel.ts:「图集不接受用户参考」判据改用 canonical icon-spritesheet,删除残留平台词的字符串清单。
- view/project-development/index.tsx:美术摘要的图片产出判据显式排除 ui-design,候选 UI 原型图不再顶掉「仅完成计划」。
- tests/appSurface/project-development.suite.ts:generateCall 支持按素材名定位,规范 / 图集 / UI 设计图入口断言改用 canonical kind。
- tests/appSurface/project-assets.suite.ts:/asset-register 与开发面板用例改用 canonical kind,面板默认值按 unknown 断言。
- tests/resourceCanvasAssetGenerationReferences.test.tsx:夹具与提交断言改用 icon-spritesheet / ui-design。
- docs/technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md:载荷表与规范通道说明改用 canonical kind,并写明平台请求词汇只留在 source.generationKind。
- docs/project-memory/shared-memory/pitfalls.md:记录前端 kind 判据与 fixture 必须一起按 canonical 成员重写。
2026-09-19 03:21:37 +08:00
k88936 603650fbf4 修复 AGC 资源 kind 收敛后的 Rust 默认值与工具栏用例
- agent/runtime_tools/media.rs:asset_kind 只在入参未声明(空白)时落通道默认值,显式认不出的值保持 Unknown 交给下游失败关闭。
- tests/project.rs:规范图 / 图集用例的入参改传 canonical kind(icon-spec / icon-spritesheet),用例名与断言同步。
2026-09-19 03:21:25 +08:00
k88936 5d20f5bfeb 修复 AGC 资源 kind 枚举化后的编译与字符串回退
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 7m9s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 7m20s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 7m24s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 8m10s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m39s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m54s
Project CI / Repository checks (pull_request) Failing after 4m43s
Project CI / Frontend tests (pull_request) Failing after 4m51s
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
- canvas_generation:参考素材合同四个 helper 改收 GameCreationAppAssetKind,删除 ui-prototype/game-background/art-spritesheet 字符串比较

- direct_runtime:删除 game-background / art-spritesheet 的参考合同字符串映射,直接传枚举

- resource_editor:音效/BGM 编辑类型判据改为比较 ResourceEditSourceKind::Manifest(BackgroundMusic),不再拼字符串 haystack

- assets/commands:画板导入 kind 解析上移到 Tauri 命令边界,import_canvas_asset_at 只接收枚举

- 测试与批量标签脚手架:kind 参数由字符串改为 GameCreationAppAssetKind,恢复编译期穷举保护

- 技术方案:登记边界同口径改为描述命令边界解析
2026-09-19 02:38:30 +08:00
k88936 af38de9fd1 Merge remote-tracking branch 'origin/master' into feat/agc-organize-assetkind
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 3m23s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 3m1s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 2m41s
Project CI / AI game creator shell Rust smoke (pull_request) Failing after 2m20s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 2m38s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m25s
Project CI / AI game creator shell web tests (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
2026-09-19 01:54:10 +08:00
k88936 18252e24b8 合并主线并保留资源kind枚举收口
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (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
合并 origin/master 的 AGC 画布参考与项目能力更新

解决 canvas 生成恢复与任务模型冲突并继续使用共享 GameCreationAppAssetKind

保留 run_id、generationKind 与外部协议字符串的独立边界
2026-09-19 01:50:09 +08:00
lhk229 1ce2c10107 删除客户端每回合四项媒体资源请求上限 (#427)
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m10s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m28s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 7m32s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m48s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m1s
Project CI / AI game creator shell Rust crates (push) Successful in 1m58s
Project CI / Repository checks (push) Successful in 5m46s
Project CI / Frontend tests (push) Successful in 7m8s
Project CI / Backend tests (push) Successful in 9m19s
Project CI / Native shell tests (push) Successful in 9m42s
Project CI / AI game creator shell web tests (push) Successful in 5m5s
- direct_tool_bridge.rs 删除 DIRECT_TOOL_BRIDGE_MAX_RESOURCE_CALLS_PER_TURN 常量、回合授权状态的 resource_request_ids 计数 map 与 resource_request_ids() 方法
- agc_create_or_derive_resource(含 agc_edit_image 委托)与 agc_remove_background 统一按回合身份与请求指纹确定性派生 operation/idempotency id,同指纹重试复用与 pending 对账语义不变
- 付费提交串行仍由 resource_generation_gate 互斥保证,成本控制交由服务端计费与泥点余额兜底
- 决策记录新增 2026-09-19 删除上限条目,2026-09-18 抠图豁免条目标记为仅作追溯
- 实施计划文档同步改为「不同请求串行提交,不设每回合计数上限」

Reviewed-on: #427
Co-authored-by: Linghong <ink29535@proton.me>
Co-committed-by: Linghong <ink29535@proton.me>
2026-09-19 01:44:48 +08:00
k88936 48caa23997 收紧AGC资源kind枚举边界
canvas生成与运行时统一使用共享GameCreationAppAssetKind

删除平台kind兼容映射与退役kind拒绝测试

更新资源kind审计里程碑与绑定文档
2026-09-19 01:28:27 +08:00
lhk229 ea141d9f17 修复持久目标模式切换测试的异步竞态
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (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 shard 1/4 (push) Successful in 5m11s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 5m4s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 5m50s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m59s
Project CI / AI game creator shell Rust crates (push) Successful in 2m28s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 4m37s
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
- developer-agent-window.suite.ts 在切换目标模式前等待按钮脱离 disabled,避免读取历史链路在途时点击落空导致 aria-pressed 断言偶发失败
2026-09-19 01:21:05 +08:00
lhk229 769740a46d Merge pull request '取消抠图工具的每回合四项媒体请求限制' (#426) from codex/remove-bg-turn-limit into master
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 6m56s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m15s
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 7m24s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 7m21s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m25s
Project CI / AI game creator shell Rust crates (push) Successful in 2m55s
Project CI / Repository checks (push) Successful in 15m5s
Project CI / Backend tests (push) Successful in 18m40s
Project CI / Native shell tests (push) Successful in 20m39s
Project CI / Frontend tests (push) Failing after 19m35s
Project CI / AI game creator shell web tests (push) Successful in 13m6s
Reviewed-on: #426
2026-09-19 00:29:51 +08:00
lhk229 b472b9eec9 取消抠图工具的每回合四项媒体请求限制
Project CI / AI game creator shell Rust smoke (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 shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
agc_remove_background 不再经过 resource_request_ids 付费计数,直接按回合身份与请求指纹确定性派生 id

抠图请求不再挤占付费 create/derive 的四项额度,幂等复用与 pending 对账语义不变

create_or_derive(含 agc_edit_image 委托)路径逐字节不变

决策记录补充 2026-09-18 抠图豁免条目

实施计划文档同步注明抠图不占四项额度
2026-09-19 00:14:01 +08:00
k88936 0d5940433d 删除资源 kind 兼容白名单
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 4m4s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (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 shard 2/4 (pull_request) Has been cancelled
服务端图片快速编辑改用共享枚举静态图片判定

移除退役 kind 的兼容放行与冗余回归测试

清理客户端恒假 art-spritesheet 判断并统一远端 kind 解析

同步 AGC kind 里程碑、实施计划与决策记录
2026-09-18 17:09:36 +08:00
suzmii ca80cb28d2 登记首批合并后的画布验收遗留问题
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 6m13s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 6m33s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m17s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 6m9s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 6m2s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m26s
Project CI / Backend tests (pull_request) Successful in 11m35s
Project CI / Frontend tests (pull_request) Successful in 11m42s
Project CI / Repository checks (pull_request) Successful in 11m40s
Project CI / Native shell tests (pull_request) Successful in 14m41s
Project CI / AI game creator shell web tests (pull_request) Successful in 9m39s
从master已合并的PR412建立后续修复范围。
区分动画与编辑等缺陷、卡片与任务浮层需求及未验证风险,不宣称已修复。
2026-09-18 15:57:32 +08:00
k88936 e5fd454bb3 收紧 AGC 资源 kind 类型边界并统一族判据
- 将 manifest 与本地导入回执的资源 kind 改为 ts-rs 生成绑定

- 集中音频与视觉资源族判据并复用 Rust 枚举方法

- 放宽 Unknown 未解析语义并清理上传类型墓碑注释

- 标记共享生成绑定并同步项目决策与实施计划
2026-09-18 15:21:19 +08:00
k88936 03d7bca9a5 Merge remote-tracking branch 'origin/master' into feat/agc-organize-assetkind
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 6m43s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 6m28s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m18s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 6m5s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m47s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 5m42s
Project CI / Repository checks (pull_request) Successful in 6m2s
Project CI / Frontend tests (pull_request) Successful in 8m48s
Project CI / Native shell tests (pull_request) Successful in 9m37s
Project CI / Backend tests (pull_request) Successful in 10m34s
Project CI / AI game creator shell web tests (pull_request) Successful in 5m29s
2026-09-18 10:43:00 +08:00
k88936 2abad3b537 修复失败测试
测试现在直接从 AGENT_RUNTIME_CANVAS_ASSET_KINDS 派生期望值,避免再次漂移。
2026-09-18 10:42:40 +08:00
k88936 fc5cea43ac Merge remote-tracking branch 'origin/master' into feat/agc-organize-assetkind
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 5m34s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 5m33s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 5m47s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m56s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 4m20s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m27s
Project CI / Repository checks (pull_request) Successful in 5m58s
Project CI / Frontend tests (pull_request) Successful in 6m49s
Project CI / Native shell tests (pull_request) Successful in 10m59s
Project CI / Backend tests (pull_request) Successful in 12m9s
Project CI / AI game creator shell web tests (pull_request) Successful in 5m5s
2026-09-18 01:55:16 +08:00
k88936 c2ab9674a0 合并资源编辑与当前主线改动
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (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
保留资源 kind 强类型与栅格 subtype 严格校验

合并抠图模式、幂等账本、恢复轮询和 PNG 透明通道校验

修正共享资源枚举在 Cocos 登记与抠图桥接中的类型传递

保留双方模板库、项目快照、发布和 Provider 重试改动
2026-09-18 01:48:13 +08:00
k88936 72cd0747a4 拒绝栅格资源 subtype 静默降级
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (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 shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
非图片族和未知 sourceSubtype 改为明确报错
补充栅格正规化回归测试并同步规范
2026-09-17 23:06:30 +08:00
k88936 3d4c7929c3 统一画布生成 kind 为共享枚举
工具白名单与 schema 改用 GameCreationAppAssetKind
canonical 输入在生成适配边界映射旧账本值
2026-09-17 22:52:38 +08:00
k88936 8dce016552 修正 manifest kind 留痕去重范围
按 manifest 来源与资产身份去重
避免不同项目相同 ID 互相抑制日志
2026-09-17 19:18:07 +08:00
k88936 64879cfcdd 合并资源规范与画布 JSON 功能
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 7m2s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 7m4s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 7m20s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 7m21s
Project CI / Backend tests (pull_request) Failing after 14s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m52s
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m5s
Project CI / Frontend tests (pull_request) Failing after 5m26s
Project CI / AI game creator shell web tests (pull_request) Failing after 4m11s
Project CI / Native shell tests (pull_request) Successful in 10m40s
合并 canonical 资源 kind 校验与按类型提示词上限。\n保留 UI JSON 预览识别、严格 UI 文档持久化和画布编辑入口。\n修复合并后 Rust 资源 kind 测试夹具并补齐定向回归用例。
2026-09-17 18:28:19 +08:00
k88936 6853ec5388 资源 kind 严格口径的文档补齐:单一解析入口与登记边界
- docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md:2026-09-17 节改为唯一解析入口 `parse_with_context()`(内部私有 `match_canonical()`,`from_str_lossy` / 公开 `from_str_or_unknown` 均已删除),并新增登记边界同口径、登记内容 kind 必须与内容相符、切片残留判据只看路径、sprite 身份不含展示串、TS 判据只留一份与「严格口径已知代价、不迁移」六条。
- docs/project-memory/shared-memory/decision-log.md:同一节补上单一解析入口、登记边界 kind 口径、判据不许散落、已知代价不补救四条决策,并把验证命令更新为实际跑过的定向用例。
2026-09-17 16:30:25 +08:00
k88936 796ad7bb51 资源 kind 只保留一个公开解析入口
- server-rs/crates/shared-contracts/src/game_creation_app/asset_kind.rs:删掉公开的 `from_str_or_unknown()`(`from_str_lossy` 的残留命名),严格匹配降级为私有 `match_canonical()`;`parse_with_context()` 成为唯一公开解析入口,`FromStr`、serde 与所有边界一律走它,认不出 canonical 值只有这一处收口并留痕。
- server-rs/crates/shared-contracts/src/game_creation_app/asset_kind.rs:词汇表用例改走唯一入口,并断言每个非 canonical 变体都留痕。
- apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs:读侧回捞 legacy kind 时不再调用第二个解析函数,直接用唯一词汇表 `ALL` 判真值(`unknown` 本身是成员,自然跳过)。
2026-09-17 16:27:48 +08:00
k88936 20b34918d3 UI 编辑器桥接用例改回真实成功路径
- apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts:`ensure_ui_design_resource_for_prototype` 的 mock 从返回 `null` 改为返回真实形状的桥接结果(asset + manifest + committedProjectRevision + created),并新增 `data-resource-view-state === 'resources.ui-editor'` 断言,让用例真的覆盖它名字里的成功路径而不是被组件吞进错误分支。
2026-09-17 16:19:38 +08:00
k88936 04e5512777 UI 编辑器文档资产的 kind + mediaType 判据收敛到共享契约唯一实现
- packages/shared/src/contracts/gameCreationApp.ts:新增 `isGameCreationAppUiDesignDocAsset(asset)`,`kind` 严格解析后再比文档 kind、`mediaType` 比 `application/json`,这是该判据的唯一定义。
- packages/shared/src/contracts/gameCreationApp.test.ts:补用例钉住判据——只满足一半(kind 对 / mediaType 不符、mediaType 对 / kind 不是文档)与 legacy kind(`UI`、`ui`、带空格、大写)一律返回 false。
- apps/ai-game-creator-shell/src/view/project-development/index.tsx:桥接结果校验与「自动打开 ui-workflow 完成资源」两处改用共享判据,删掉本地 kind/mediaType 双条件。
- apps/ai-game-creator-shell/src/features/ui-editor/uiDesignResourceBridge.ts:`findLinkedUiDesignResource` 的过滤条件改用共享判据。
- apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx:缩略图文档分支改用共享判据(原来只看 mediaType,kind 不同也能命中)。
2026-09-17 16:19:26 +08:00
k88936 0510cd8d9c AGC 壳把资源 kind 的登记与解析边界统一到严格口径
- assets.rs:新增 `registration_asset_kind()`,空白入参落中性 `image`、非空认不出严格收口成 `Unknown` 并留痕;`import_canvas_asset_at` 改走该函数。
- commands.rs:`imported_platform_asset_kind` 与 `register_local_asset` 一并委托给 `registration_asset_kind()`,删掉两处各写一遍的 trim/严格解析分支。
- agent/generation/canvas_generation.rs:切片残留登记判据改为只看 `assets/art-spritesheet-slices/` 路径——历史切片刻的 kind 是非 canonical 值,严格解析后收口成 `unknown`,再带 kind 判据会漏掉半成品登记并静默跳过回填;补平台词汇映射用例钉住别名与未映射值落点。
- project/resource_editor.rs:栅格归一化的源 subtype 收口到图片族成员(音频/视频/文档/字体等一律按图片登记),Agent 回执派生物写回 `Document` 而不再主动写 `Unknown`;补派生 kind 用例。
- ui_editor/workflow.rs:新增 `same_sprite_asset_identity()`,比较 sprite 身份时忽略随 kind 派生的 `metadata.asset_type`,避免对既有 State 重跑工作流误报资源冲突。
2026-09-17 16:19:00 +08:00
k88936 7a237431e0 资源 kind 单源与严格口径的文档同步
- docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md:新增 2026-09-17 权威节,写明唯一词汇表、严格解析、app_log! 留痕与生成目录搬家。
- docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md:删掉「alias 表大小写不敏感」的旧口径,改为严格等值解析 + 原始串留痕,并把跨端守卫改为编译期绑定。
- docs/project-memory/shared-memory/decision-log.md:新增 2026-09-17 条目记录四项决策与验证命令。
- docs/project-memory/shared-memory/pitfalls.md:新增 ts-rs 生成目录搬家后必须同步忽略规则的坑;旧别名表相关条目补上现行口径,避免后人照旧重加别名表。
- docs/project-memory/plans/ / todos/:把 kind-observability、壳内生成目录、已删除的交叉守卫用例等过期描述改成当前口径。
2026-09-17 15:46:58 +08:00
k88936 6ed3772ff5 资源 kind 用例与 fixture 跟随严格口径(非 canonical 值一律停在待归类)
- packages/shared/src/contracts/gameCreationApp.test.ts:新增严格解析用例(认不出的原值 console.warn 留痕、返回 unknown),分类映射与自愈用例改用 canonical ui-design / icon,并断言 kind:'ui' 收口成 unknown 后落 unclassified。
- tests/resourceTypePanel.test.tsx / tests/resourceClassificationPanel.test.tsx:自愈用例的 fixture kind 由 ui 改为 ui-design,让「显示口径 ≠ 落盘口径」这条判据继续真的被验证。
- tests/projectResourceLiveIntegration.test.tsx:fixture kind 改为 ui-design;用例按 canonical UI 设计图带「(待视觉验收)」的标签定位按钮,并把用例名与注释改回真实口径。
- tests/resourceCardPreviewRealManifest.test.ts:真机 61 条资产的取证结论改为严格口径——57 条 ui 与 game-entry 停在待归类,明确栏目只由落盘 category 决定;自愈断言改用 ui-design。
- tests/appSurface/home.suite.ts / project-development.suite.ts:fixture 里的 art-spritesheet / game-background / animation / ui-prototype / ui 改为 canonical icon-spritesheet / scene / character-animation / ui-design / icon。
- tests/projectResourceProjectionModel.test.ts / projectResourceLiveUpdateModel.test.ts:fixture kind 由 game-entry / asset 改为 code / image。
2026-09-17 15:46:43 +08:00
k88936 a86ead93e4 资源登记草稿的推断 kind 收敛到 canonical 词汇
- projectArtifactSummaries.ts:inferProjectFileAssetDraft 的 json 分支由 data 改为 document、判不出扩展名时由 asset 改为 unknown,与 Rust 侧 uploaded_asset_kind 同口径——/asset-register 的 kind 会原样进 register_local_asset 的严格解析,写非 canonical 值只会被判成认不出并留痕。
2026-09-17 15:46:34 +08:00
k88936 b0ce384b8d 前端资源 kind 改为只消费 Rust 生成的 union,删除壳内第二份实现
- packages/shared/src/contracts/gameCreationApp.ts:删除手写 GAME_CREATION_APP_CANONICAL_ASSET_KINDS、GameCreationAppCanonicalAssetKind、GAME_CREATION_APP_LEGACY_ASSET_KINDS 与 canonicalGameCreationAppAssetKind(),改为 re-export ts-rs 生成的 union。
- packages/shared/src/contracts/gameCreationApp.ts:新增穷举 GAME_CREATION_APP_ASSET_KIND_MEMBERS / GAME_CREATION_APP_ASSET_KINDS、isGameCreationAppAssetKind 与严格解析 parseGameCreationAppAssetKind(认不出的原值 console.warn 留痕并返回 unknown)。
- packages/shared/src/contracts/gameCreationApp.ts:分类映射表与 gameCreationAppAssetCategoryForKind 改按生成 union 穷举,unknown 落 unclassified。
- packages/shared/src/contracts/generated/GameCreationAppAssetKind.ts:ts-rs 生成物从壳内私有目录搬到 packages/shared(跨端契约目录)。
- apps/ai-game-creator-shell/src/contracts/assetKind.ts:删除这份壳内重复实现,消费方改从 packages/shared 导入。
- apps/ai-game-creator-shell/src/features/project-workspace/resourceReferences.ts / features/ui-editor/uiDesignResourceBridge.ts / view/project-development/index.tsx:导入点改到 packages/shared 的同一份 parser 与常量。
- apps/ai-game-creator-shell/tests/assetKindCanonicalMapping.test.ts:删除「正则解析 Rust 源码比对两份手写表」的守卫用例,跨语言一致性改由生成 union 在编译期保证。
- .prettierignore / .eslintrc.cjs:忽略规则跟着生成目录一起搬到 packages/shared/src/contracts/generated。
2026-09-17 15:45:24 +08:00
k88936 b56566f40e AGC 壳把资源 kind 留痕接到 app_log! 并把各解析边界统一为严格口径
- main.rs:新增 register_non_canonical_asset_kind_reporter(),把 shared-contracts 的原始串与上下文落到 app_log!,按 (原始串, 上下文) 去重;main() 启动时注册。
- commands.rs:imported_platform_asset_kind 改为直接 parse_with_context,不再先经别名表收口;用例改为断言 UI / ui / mystery / game-art / ui-prototype 等非 canonical 值一律落 unknown。
- assets.rs:画板/上传等用例的 kind 断言改用 GameCreationAppAssetKind::ALL 穷举口径。
- project/manifest.rs:读 manifest 的留痕去重键由「只有原始 kind」改成「原始 kind + 资产身份」,避免第二个写旧值的资产被静默吞掉。
- project/manifest.rs:from_str_lossy 调用点改名 from_str_or_unknown。
- agent/generation/canvas_generation.rs:画板 assetKind 的兜底分支改为 parse_with_context,非 canonical 值留痕收口为 unknown。
- project/version_resource_replacement.rs:subtypeEqual 改为 canonical kind 直接比较,去掉中间归一。
2026-09-17 15:44:02 +08:00
k88936 6782d0ea23 GameCreationApp 资源 kind 词汇表收敛为 Rust 单源并接上可注册留痕回调
- asset_kind.rs:新增声明宏,由一张 kind 表派生出枚举、线上值 as_str、ALL、严格解析与 ts-rs 绑定,仓库里不再有第二份 kind 列表。
- asset_kind.rs:from_str_lossy 改名 from_str_or_unknown 并去掉 trim / lowercase 归一,parse_with_context 只做严格等值匹配,认不出的原值不查别名、不迁移。
- asset_kind.rs:留痕改为可注册回调 set_non_canonical_asset_kind_reporter,把原始串与调用上下文交给壳层日志,未注册时静默。
- game_creation_app.rs:删除手写 GAME_CREATION_APP_CANONICAL_ASSET_KINDS 与 canonical_game_creation_app_asset_kind 别名表(含 font → document 特例),分类映射表按枚举成员穷举。
- shared-contracts/Cargo.toml:删除 kind-observability feature 与 tracing 依赖,留痕不再靠 tracing。
- api-server/editor_project.rs:图片编辑白名单注释改成「API 兼容面」,不再自称 kind 别名表。
2026-09-17 15:43:51 +08:00
k88936 e6092dda6f Merge remote-tracking branch 'origin/master' into feat/agc-organize-assetkind
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 6m6s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 3m19s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 6m23s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 6m9s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 5m55s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m36s
Project CI / Repository checks (pull_request) Successful in 5m36s
Project CI / Frontend tests (pull_request) Failing after 7m10s
Project CI / Backend tests (pull_request) Successful in 13m10s
Project CI / Native shell tests (pull_request) Successful in 11m34s
Project CI / AI game creator shell web tests (pull_request) Successful in 5m38s
# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
2026-09-17 13:52:45 +08:00
k88936 5f13698129 asset.list 的 kind 过滤改为拒绝非 canonical 值
- direct_tool_bridge.rs:`kind` 收口后既不是 canonical、也不是字面 `unknown` 时直接返回工具错误并带上原值,不再静默过滤出空列表。
- direct_tool_bridge.rs:新增用例,钉住"项目访问前就报错"与"字面 `unknown` 仍是合法过滤值"。
- direct_tools_mcp.rs:schema 描述改为必须是 canonical kind、非 canonical 值会被拒绝。
2026-09-17 13:42:22 +08:00
k88936 29ba9a358e 字体 kind 提升为 canonical 并去掉 font 别名
- game_creation_app.rs:`GAME_CREATION_APP_CANONICAL_ASSET_KINDS` 加入 `font`(17 → 18 项),分类表补 `font → document`,删除 `"font" => "document"` 别名。
- gameCreationApp.ts:TS 侧同步加入 `font`、删除别名表条目、补 `font: 'document'` 分类,并同步别名表注释。
- 测试:两侧用例改为断言 `font` 是 canonical 成员(大小写仍不敏感)、栏目仍是 document。
- 理由:字体上传写入侧直接落 `font`,再把它当别名会让「落盘值集合」与「canonical 集合」分叉;严格解析下写入侧的值必须本身就是 canonical。
2026-09-17 13:42:05 +08:00
k88936 3ab56e6cfa 非 canonical 资源 kind 严格收口并留痕原始串
- manifest.rs:读 manifest 时若存在 `unknown` kind,从原始 JSON 回捞 `assets[].kind` 原值,连同资产 ID、路径写进应用日志;同一原值每进程只记一次,避免刷屏。
- recovery_tests.rs:新增用例钉住回捞范围(只报非 canonical 值,跳过 canonical 与字面 `unknown`;结构异常返回空表)。
- commands.rs:`imported_platform_asset_kind` 收口平台 `assetKind` 时,非 canonical 原值一并留痕,供后续定位平台侧 legacy 写入点。
- shared-contracts/Cargo.toml:`ts-bindings` 不再顺带打开 `kind-observability`,改由 AGC 壳显式声明,日志开关不再随绑定需求被动启停。
- 口径:kind 保持严格解析(无 trim/lowercase、无别名表、无迁移),认不出的值一律收口 `unknown` 并留痕,不做 fallback。
2026-09-17 13:41:41 +08:00
k88936 56c4faa8ee 清理项目视觉资产判断里已不可达的旧 kind 字面量
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 4m58s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 4m44s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 4m26s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m3s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 4m0s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m50s
Project CI / Repository checks (pull_request) Failing after 14s
Project CI / Frontend tests (pull_request) Successful in 6m3s
Project CI / AI game creator shell web tests (pull_request) Successful in 6m3s
Project CI / Native shell tests (pull_request) Successful in 11m11s
- projectAssetSummaries.ts:`isProjectVisualAsset` 去掉 `sprite` / `ui` / `background` 三个旧 kind 判断。
- 理由:manifest kind 由 Rust 枚举序列化,只会是 canonical 成员或 `unknown`,这三个旧字面量在 TS 侧永远无法命中;图像与视频资产仍由 mediaType 分支覆盖。
2026-09-16 23:31:01 +08:00
k88936 d23c808376 新建 UI 设计资源复用无冲突编号规则
- ui_editor/resource_bridge.rs:`next_ui_design_path` 提升为 `pub(crate)` 并补注释,作为两个写入侧唯一的「UI 设计 N」命名规则。
- commands.rs:`create_ui_design_resource` 不再按已登记文档数直接取名,改为复用该规则,resourceId 与文件名同一编号。
- tests/project.rs:新增已存在 `ui/UI 设计 1.json` 时仍能创建并落到编号 2 的回归用例。
2026-09-16 23:24:15 +08:00
k88936 41930b858e 修正陶泥儿运行态图片筛选残留的旧 kind 字面量
- direct_runtime/mod.rs:`direct_game_sources_referenced_taonier_assets` 的 manifest 兜底由 `art-spritesheet | art-spritesheet-slice | game-background` 改为 `IconSpritesheet | Icon | Scene`,恢复对项目自定义图集与背景的收录。
- direct_runtime/mod.rs:同处与 `direct_registered_taonier_runtime_image_paths` 的「切片不得绕过严格校验」排除条件改用 `GameCreationAppAssetKind::Icon`,并顺手把 `icon-spec` 比较改为枚举成员。
- 理由:kind 收紧为枚举后 `as_str()` 只会返回 canonical 名,旧字面量恒不命中,兜底筛选与切片排除同时失效。
2026-09-16 23:13:21 +08:00
k88936 9dccf5318e UI 原型 kind 判断改用共享常量
- uiDesignResourceBridge.ts:原型 kind 比较由裸字符串 `'ui-design'` 改为 `GAME_CREATION_APP_UI_DESIGN_ASSET_KIND`,与 manifest kind 契约保持同一处定义。
2026-09-16 23:10:59 +08:00
k88936 f216381619 修正 UI 编辑器入口对 canonical ui-design 原型的判断
- packages/shared:新增 `GAME_CREATION_APP_UI_DESIGN_ASSET_KIND`(`ui-design`)并让 canonical kind 列表与分类表引用它,避免散落裸字符串。
- project-development/index.tsx:`openResourceUiEditor` 与 `selectedResourceOpensUiEditor` 的原型判断由旧值 `ui-prototype` 改为该常量,恢复原型图的「UI 编辑器」入口。
- resourceProjectionModel.ts:`isPendingUiPrototype` 同步改用该常量,恢复待视觉验收标注。
- project-development.suite.ts:候选界面图 fixture 改用 canonical kind,并补一条原型图打开 UI 编辑器桥接的回归用例(去掉修正即转红)。
2026-09-16 23:10:31 +08:00
k88936 d454d3fa4b 修正 asset.list 工具示例 kind 到 canonical 词汇
- direct_tools_mcp.rs:`agc_list_registered_assets` 的 kind 过滤示例把旧值 `art-spritesheet-slice` 换成 canonical 的 `icon-spritesheet`。
- 理由:manifest kind 收紧为枚举后,旧值在过滤前已收口为 `unknown`,按示例传参只会得到空结果。
2026-09-16 23:03:44 +08:00
k88936 a5ee45aa4e 构建脚本依赖不再打开 shared-contracts 的 ts-bindings
- Cargo.toml:`[build-dependencies]` 的 shared-contracts 去掉 `features = ["ts-bindings"]`,只保留 `[dependencies]` 那份。
- build.rs 只用 `GameCreationAppAgentGroup` 与 `new_game_creation_app_seed_tasks`,不需要 ts-rs 导出,没必要让构建脚本图额外编译 ts-rs。
2026-09-16 23:01:51 +08:00
k88936 d333f783fe 修正资源 kind 解析边界对非字符串输入的日志
- assetKind.ts:非字符串输入不再走 `String(raw)` 字符串化,改为记录 `rawType` 并直接收口为 `unknown`。
- assetKind.test.ts:新增非字符串输入用例,钉住新的日志形状。
2026-09-16 23:00:42 +08:00
k88936 faab318127 删除资源引用缩略图里已不可达的 ui kind 分支
- ResourceReferenceInput 缩略图:删掉 `kind === 'ui'` 判断与只服务它的 `kind` 变量,UI 设计文档仍由 `mediaType === 'application/json'` 识别。
- 理由:manifest kind 由 Rust 枚举序列化,只会是 canonical 成员或 `unknown`,该分支永远无法命中。
2026-09-16 23:00:07 +08:00
k88936 a4296a4bdd 合并 origin/master 并收敛 DirectProject 引用渲染到 canonical user item
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 7m17s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 7m15s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 7m45s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 7m59s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m8s
Project CI / Repository checks (pull_request) Failing after 9s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m48s
Project CI / AI game creator shell web tests (pull_request) Successful in 6m24s
Project CI / Frontend tests (pull_request) Successful in 15m29s
Project CI / Native shell tests (pull_request) Successful in 19m38s
- .eslintrc.cjs 冲突取双方 ignore 并集,保留 contracts/generated/** 与 project-workspace/generated/**。
- 实施计划文档冲突保留双方内容:2026-09-15 资源 kind 枚举化权威口径与 master 新增计划段并存。
- resourceReferences.ts 冲突合并双方 import:kind 解析入口与 canonical DirectCodexUserItem 类型同时保留。
- project-development.suite.ts 冲突合并双方 import:UI 设计文档 kind/mediaType 常量与 ProjectSupervisorView 同时保留。
- decision-log.md 冲突取双方条目,并补记 DirectProject 引用渲染收敛到 canonical user item 的决策。
- 接受 master 退役 agent/direct_codex_references.rs,把 UI 设计文档引用生成代码上下文的 prompt 注入迁入 agent/direct_codex_user_item/wire.rs。
- assets.rs 资源登记 kind 参数收紧为 GameCreationAppAssetKind,相应测试 fixture 改用正式枚举成员。
- 同步 project-memory 实施计划、里程碑与扫描清单中的模块路径。
2026-09-16 21:01:20 +08:00
k88936 7eee89ded3 同步 AGC 资源 kind 枚举化的实施计划、里程碑与扫描清单
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 5m22s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 5m16s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 5m4s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m39s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 4m40s
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m1s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m3s
Project CI / Frontend tests (pull_request) Successful in 4m35s
Project CI / Native shell tests (pull_request) Successful in 6m44s
- 实施计划:记录已落地内容(kind 由 `String` 收紧为枚举的破坏性变更、写入与读取统一正式成员、资源编辑源身份词汇表、`cargo test export_bindings` 生成路径、TS 穷举派生)
- 实施计划:修正「明确不修改」与验证命令,去掉与当前代码不符的表述
- 里程碑:验收标准除旧 alias 读边界一项外全部勾选,并补记 ts-rs 生成与退役类型导出两项证据
- 扫描清单:把「当前未决项」逐条落成结论(`asset`、`art-spritesheet*`、`game-*`、`ui*`、`Unknown` trace、生成路径),只保留 CI verify 接入一项待办
2026-09-16 19:08:28 +08:00
k88936 f9bc2d917c AGC 资源 kind 集合改为从生成 union 穷举派生
- assetKind.ts:新增 `GAME_CREATION_APP_ASSET_KIND_MEMBERS: Record<GameCreationAppAssetKind, true>`,生成 union 增删成员时会直接编译报错
- assetKind.ts:`GAME_CREATION_APP_ASSET_KINDS` 由该表派生,不再手写第二份字面量列表
- assetKind.ts:新增 `isGameCreationAppAssetKind` 类型守卫,`parseGameCreationAppAssetKind` 复用它
- view/project-development/index.tsx:资源引用的 `sourceSubtype` 只传正式 kind 成员,任务产物等非 manifest 来源传 `null`
- tests/assetKindCanonicalMapping.test.ts:两侧 kind 目录比对前先展开 Rust 常量引用(`ui-design-doc` 是常量),修正此前恒失败的两条;删除测试体里游离的 `'font',` 表达式
2026-09-16 19:07:58 +08:00
k88936 b96cd4b7c2 ts-rs 绑定统一走 cargo test export_bindings,删除退役类型导出
- asset_kind.rs:`GameCreationAppAssetKind` 自带 `#[ts(export, export_to = ../apps/ai-game-creator-shell/src/contracts/generated/)]`,`cargo test export_bindings` 即可复现生成文件
- binding.rs:删除手写的 `exports_ui_editor_types` 测试,不再由测试就地重写生成文件或重建退役类型
- binding.rs:`BindingChange` / `BindingDTO` 去掉 `TS` derive 与 `ts(export_to)`,只作为内部结构使用,不再导出退役的前端绑定
- binding.rs:清理仅为该测试存在的 `ts_rs` / `GameCreationAppAssetKind` 导入
- .prettierignore、.eslintrc.cjs:忽略 `src/contracts/generated/`,保留 ts-rs 原始格式使生成幂等且不污染工作树
- GameCreationAppAssetKind.ts:按生成器原始格式更新(与上述路径一致,无内容漂移)
2026-09-16 19:07:33 +08:00
k88936 f185d21f55 资源编辑源身份改用独立词汇表,只传正式 kind 成员
- resource_editor.rs:新增 `ResourceEditSourceKind`,把源身份拆成 `Manifest(GameCreationAppAssetKind)` / `ProjectVersion` / `AgentResultText`
- resource_editor.rs:`ResourceEditSourceSnapshot.asset_kind` 由 `String` 收紧为该枚举,项目版本与 Agent 回执不再冒充 manifest kind
- resource_editor.rs:`infer_resource_edit_source_asset_kind` 直接返回枚举成员,`art-image` / `svg` / `text` 收口为 `Image` / `Image` / `Document`
- resource_editor.rs:栅格源归一化只接受正式枚举成员,`task-artifact` 特判删除,未知值按 `Image` 收口
- resource_editor.rs:账本、HTTP 载荷与提示词边界改用 `as_str()`,manifest 写回改用 `manifest_kind()`
- resource_editor.rs:`source_subtype` 字段补充「只接受 manifest 正式 kind」的文档说明,测试 fixture 同步改用枚举成员
2026-09-16 19:07:23 +08:00
k88936 a61b0cce81 收敛 AGC 资源 kind 写入与读取到正式枚举成员
- assets.rs:`register_local_asset_at` / `register_local_asset_entry` 改为接收 `GameCreationAppAssetKind`,画板导入与画板同步在边界解析 kind
- assets.rs:`infer_canvas_export_asset_kind` 直接返回枚举成员,去掉仅供兜底的 canonical 归一
- commands.rs:资源登记按 `asset.register` 上下文解析 kind,字体导入写 `Font`,本地文件类型表与平台导入归一改用枚举
- direct_runtime.rs:直连游戏工程产物统一写 `Code`,图集切片读取按 `Icon` 叠加切片路径判定,陶泥儿美术身份改收枚举
- canvas_generation.rs:新增 `platform_art_asset_manifest_kind` 收口平台美术请求词汇,切片登记 category 改用 `Icon.as_str()`
- manifest.rs:必填视觉资产校验改用 `IconSpec` / `UiDesign` / `IconSpritesheet`,修正此前与写入侧对不上的比对
- project_gates.rs、autonomous_policy.rs、recovery_scan.rs、direct_tool_bridge.rs、ui_editor workflow/persistence/resource_bridge:读取侧与 fixture 改用正式枚举成员
- tests:资源相关 fixture 与断言改用正式枚举成员
2026-09-16 19:07:12 +08:00
k88936 2a75a2d2c8 回退资源 kind 绑定断言的误改
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (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 shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
ts-rs 导出内容是双引号,测试重新生成后再校验,恢复双引号断言。
2026-09-16 17:05:03 +08:00
k88936 512f669f57 修正资源引用入口的源码断言
正则改为匹配 parseGameCreationAppAssetKind(selectedResource.subtype) 的新调用形状。
2026-09-16 16:59:51 +08:00
k88936 21dbbb4c3c 清理资源桥接测试的旧 kind fixture
将有效分类投影用例的资源 kind 改为 UiDesign 枚举与 ui-design 断言。

移除遗留 ui 字符串 fixture,使 AGC 壳测试恢复编译。
2026-09-16 16:53:27 +08:00
k88936 55d0c99e2b 修正资源 kind 转义测试期望
转义后的 ui-design 应解析为 UiDesign 枚举。
2026-09-15 20:57:14 +08:00
k88936 3907a8bf74 补齐资源 kind 字符串字段转换
资源编辑请求中的 sourceSubtype 统一从 AssetKind 显式转换字符串。
2026-09-15 20:52:53 +08:00
k88936 46dfa2cffb 修复资源枚举迁移后的测试类型错误
将字体、角色和资源图 fixture 改为 AssetKind 枚举。

将资源 kind 回传到字符串字段时显式使用 to_string。
2026-09-15 20:45:55 +08:00
k88936 97e2b26900 简化资源引用 kind 来源
引用资源时直接解析已存在的 subtype,移除无效 category 回退。
2026-09-15 20:14:13 +08:00
k88936 39e33f7a5b 更新上传资源 kind 枚举测试
按 Font 与 Unknown 返回值修正上传类型断言。

使用枚举字符串视图校验 canonical kind 集合。
2026-09-15 20:01:20 +08:00
k88936 040f646480 统一 ts-rs workspace 依赖版本
在 server-rs workspace 集中声明 ts-rs 版本。

shared-contracts 通过 workspace 依赖引用可选 ts-rs。
2026-09-15 19:53:06 +08:00
k88936 8b007c637d 修正图集切片读取侧枚举判断
恢复校验和测试筛选统一识别 Icon 资源 kind。

部分登记探测限定在图集切片路径,避免误判普通图标。
2026-09-15 19:48:48 +08:00
k88936 f0a62c6306 迁移资源桥接测试 fixture 到枚举
将图集切片、角色动画和规格资源 fixture 改用对应 AssetKind 变体。
2026-09-15 19:45:51 +08:00
k88936 30edfee00e 修复 Codex 资源引用测试 fixture
将 manifest 资源 kind 字符串 fixture 迁移为 Character 枚举。
2026-09-15 19:41:41 +08:00
k88936 c2da8e184e 修正 UI 工作流源资源枚举校验
工作流源图校验与 fixture 统一使用 UiDesign。

更新失败提示中的 manifest kind 名称。
2026-09-15 19:36:29 +08:00
k88936 a19319d815 修正 UI 原型资源桥接枚举使用
生产校验与测试 fixture 改用 GameCreationAppAssetKind::UiDesign。

保留 ui-prototype 作为本地文件路径标识。
2026-09-15 19:35:04 +08:00
k88936 d07ea186fa 收紧资源 kind 测试专用导入
仅在测试构建中导入 GameCreationAppAssetKind,避免正式构建产生未使用警告。
2026-09-15 19:33:13 +08:00
k88936 80caec373a 修正资源 kind 绑定测试断言
按 ts-rs 生成的单引号联合成员校验绑定内容。
2026-09-15 19:32:12 +08:00
k88936 3caefd6716 修复资源 kind 转义字符串反序列化
为 AssetKindVisitor 增加 visit_string 转发。

补充 JSON 转义 kind 的反序列化回归测试。
2026-09-15 19:31:21 +08:00
k88936 4d0dd2d721 收紧 UI 资源桥接的 manifest kind
UI 原型与文档链接改用生成枚举边界
删除 ui-prototype 的主动 alias 使用并更新测试清单
2026-09-15 18:06:27 +08:00
k88936 2befaaef9d 接入 AGC shell TS 资源 kind 边界
新增消费 Rust 生成绑定的未知值解析模块
迁移资源引用与画布引用入口并补充日志测试
更新逐项迁移清单与实施计划
2026-09-15 18:00:34 +08:00
k88936 20b0bf4dd7 补充 AGC manifest 未知 kind 验收
验证旧值读取为 Unknown 且序列化为 unknown
明确不执行 alias 迁移
2026-09-15 17:50:33 +08:00
k88936 b4fd8d9b8b 收紧 AGC manifest 资源 kind 字段
将 manifest 条目 kind 改为 Rust 枚举并把未知值收口为 Unknown
修正字体上传与图集切片的正式资源 kind
保持 source.kind 与 generationKind 等其他字符串字段不变
2026-09-15 17:37:28 +08:00
k88936 fe85fa2a62 建立 AGC 资源 kind Rust 枚举与 TypeScript 绑定
新增 GameCreationAppAssetKind 及 Unknown 解析边界
通过 ts-rs 生成 shell 专属 TypeScript 联合类型
补充未知值上下文日志与 Rust/绑定生成测试
2026-09-15 16:20:52 +08:00
k88936 b14a533b17 新增AGC资源kind绑定实施计划
限定首个代码切片为 Rust enum 与 ts-rs 生成边界。

明确不修改 manifest 字段和其他资源字符串领域。

列出绑定、serde round-trip 与定向验证门禁。
2026-09-15 15:29:37 +08:00
k88936 42969b2218 细化AGC资源kind构造器审计
补充 Rust manifest 构造器逐处迁移清单。

补充 shell TypeScript 裸 assetKind 消费方清单。

区分图集、工作流和 manifest kind 的处理边界。
2026-09-15 15:24:46 +08:00
k88936 c4ee75a1af 补充AGC资源kind词汇审计清单
记录 manifest kind 的 Rust 写入边界与 TypeScript 消费方。

区分资源 kind 与 source/generation/workflow 等字符串领域。

标注 font、audio、sound-effect、background-music 及旧值的生产状态。

通过文档索引、编码与 diff 检查。
2026-09-15 15:19:58 +08:00
k88936 6f2137cbb0 补充AGC资源kind枚举化规范
新增资源 kind 枚举化主规范与边界。

新增词汇审计、Rust 绑定、写入清理三个里程碑。

新增 shell-only 资源 kind 扫描 todo 清单。

通过文档索引、编码与 diff 检查。
2026-09-15 15:12:24 +08:00
k88936 70a2246e8b 移除 UI 文档引用新增测试
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 4m30s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 4m59s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 4m24s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m50s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 4m28s
Project CI / Repository checks (pull_request) Failing after 13s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m7s
Project CI / AI game creator shell web tests (pull_request) Failing after 3m22s
Project CI / Frontend tests (pull_request) Failing after 3m51s
Project CI / Native shell tests (pull_request) Successful in 7m4s
删除 Direct Codex UI 文档 fixture 与专门测试用例

保留生产引用逻辑和现有业务测试
2026-09-14 21:41:53 +08:00
k88936 b7e3dac661 统一引用 UI 文档资产类型常量
前端入口、资源桥接与引用预览统一使用共享常量

Rust 持久化与测试复用共享契约常量

补齐 UI 编辑器入口 fixture 的新资产类型
2026-09-14 21:36:43 +08:00
k88936 2e4a1996c8 同步 UI 编辑器入口测试资产类型
将项目开发 UI 编辑器回程测试 fixture 更新为 ui-design-doc
2026-09-14 21:12:03 +08:00
k88936 fe4e952853 更新 UI 文档资源编辑器入口
将 ui-design-doc 资源纳入选中资源的 UI 编辑器按钮判定

保留 ui-prototype 图片资源的编辑器桥接入口
2026-09-14 21:04:08 +08:00
k88936 18654b6806 同步 UI 文档资产共享契约与前端识别
共享契约新增 ui-design-doc 分类及中英文映射

前端编辑器与资源桥接严格识别文档资产

更新技术方案、实施计划和长期决策记录
2026-09-14 20:45:04 +08:00
k88936 ee7d00c0b1 统一 UI 编辑器文档资产类型
新增 ui-design-doc 与 application/json 常量

创建、桥接、工作流和持久化统一使用严格资产类型
2026-09-14 20:44:49 +08:00
k88936 9cd1a94369 接入 UI 文档引用代码上下文生成
Direct Codex 引用 ui-design-doc 时生成带文档的 JS 文件

生成成功或失败信息追加到当前 prompt

补充成功、失败与 ui-prototype 隔离测试
2026-09-14 20:44:49 +08:00
k88936 470e85c0ff Add UI-specific prompt rendering for design assets in resource references 2026-09-14 20:44:49 +08:00
841 changed files with 107488 additions and 23957 deletions
+2
View File
@@ -4,3 +4,5 @@
src/components/AdventurePanel.tsx
src/data/customWorldCharacterLoadout.ts
dist_check_monster_position/**
# 固定上游UTF-8测试刻意包含U+FFFDupstream-integrity.test.mjs逐字节验证来源hash。
apps/ai-game-creator-shell/src-tauri/vendor/codex-utils-path-uri/src/api_path_string_tests.rs
+4
View File
@@ -235,6 +235,10 @@ VITE_DEBUG_MODE=""
# This is read by api-server and exposed through /api/runtime/frontend-config.
GENARRATIVE_ENABLE_IMAGE_EDITOR_AGENT_SIDEBAR="false"
# 官网客户端下载检测渠道:dev、release 或自定义渠道;修改后重启 API 服务。
# Windows/macOS 是系统维度,不填写 dev-win/dev-mac。
GENARRATIVE_CLIENT_DOWNLOAD_CHANNEL="dev"
# Optional: official VikingDB credentials for regenerating build-tag similarities
# with the Python embedding script. The script auto-loads `.env.local` and uses
# the fixed `bge-large-zh` embedding model.
+3
View File
@@ -23,3 +23,6 @@
*.meta text
*.anim text
*.controller text
# Rust ts-rs 生成的共享契约:保留在仓库中供 TS 消费,但不作为手写源文件统计。
packages/shared/src/contracts/generated/** linguist-generated=true
+51 -78
View File
@@ -31,28 +31,26 @@ env:
RUSTC_WRAPPER: ''
CARGO_BUILD_RUSTC_WRAPPER: ''
# job 声明顺序就是 runner 领取顺序,因此把最长尾的客户端 Rust 门禁排在前面,
# job 声明顺序就是 runner 领取顺序,因此把两条最长尾的客户端 Rust lane 排在前面,
# 让它在最少的等待下占用并发槽位;其余 job 按时长递减排列。
#
# 客户端(微信壳 / Expo 移动壳 / Tauri 桌面壳 / AI 游戏创作壳)门禁原先全部串在
# `Native shell tests` 一个 job 里,实测 18 分 37 秒。现在按门禁组拆成一个分组一个 job:
# `Native shell tests`(契约 + H5 / 微信 / 移动 / 桌面壳门禁 + 发布构建 smoke)、
# `AI game creator shell web tests`typecheck + 壳内测试)、`AI game creator shell
# Rust shard 1/4` 到 `4/4`AGC 壳 bin 单测按名单分 4 片)、`AI game creator shell
# Rust smoke`agent-run smoke)与 `AI game creator shell Rust crates`AGC 壳依赖的
# Rust lane 1/2`、`lane 2/2`(每条 lane 顺序运行两片 AGC 壳 bin 单测)、`AI game creator
# shell Rust smoke`agent-run smoke)与 `AI game creator shell Rust crates`AGC 壳依赖的
# 共享 / 平台 crate 测试)。各自的命令与拆分前逐一对应,本地
# `npm run check:native-shells` 仍是同一条串行序列。
#
# AGC 壳的 bin 单测(2466 条)按名单分 4 片、一片一个 job片内保持 `--test-threads=1`
# (当年线程并行会互相干扰的是进程内后台锁与异步终态),片与片之间靠 job 级并发摊开。
# 不要改回「一个 job 里多进程并行这几片」:同一容器内它们会争抢共享 HOME、target 与固定
# 临时路径,实测比整套串行还慢。每个分片 job 都会自校验「片并集等于全集且互斥」。
# AGC 壳的 bin 单测(2466 条)按名单分 4 片、片内保持 `--test-threads=1`;两条 lane
# 各自顺序运行两片,让一次 job 只做一次依赖预热,同时避免同一容器内多进程并行争抢
# 共享 HOME、target 与固定临时路径。每个分片调用仍会自校验「片并集等于全集且互斥」。
jobs:
# AGC 壳自身的 Rust bin 单测分片,4 片各自独立 job 并发执行、片内仍保持
# `--test-threads=1`。这里不装 npm 依赖:壳 Rust 门禁只用 cargo 与 node 内建模块,
# 也只需要 AGC 壳自己那份锁定依赖。
ai-game-creator-shell-rust-shard-1:
name: AI game creator shell Rust shard 1/4
# AGC 壳自身的 Rust bin 单测由两条 lane 承载,每条 lane 顺序运行两片;这里不装 npm
# 依赖:壳 Rust 门禁只用 cargo 与 node 内建模块,也只需要 AGC 壳自己那份锁定依赖。
ai-game-creator-shell-rust-lane-1:
name: AI game creator shell Rust lane 1/2
runs-on: genarrative-ci
steps:
- name: Checkout full history from Gitea
@@ -87,41 +85,11 @@ jobs:
- name: Run AI game creator shell Rust shard 1/4
run: npm run check:native-shells:agc-rust-shard-1
ai-game-creator-shell-rust-shard-2:
name: AI game creator shell Rust shard 2/4
runs-on: genarrative-ci
steps:
- name: Checkout full history from Gitea
env:
GENARRATIVE_GITEA_FETCH_DEPTH: '0'
GENARRATIVE_GITEA_TOKEN: ${{ github.token }}
run: genarrative-gitea-checkout
- name: Validate preinstalled CI job image and sandbox
run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh
- name: Prepare AI game creator shell Rust dependencies
shell: bash
run: |
set -euo pipefail
for attempt in $(seq 1 5); do
if cargo fetch --locked \
--target x86_64-unknown-linux-gnu \
--manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml; then
break
fi
if [[ "${attempt}" -eq 5 ]]; then
echo 'AI game creator shell Cargo dependency fetch failed after 5 attempts.' >&2
exit 1
fi
sleep $((attempt * 2))
done
- name: Run AI game creator shell Rust shard 2/4
run: npm run check:native-shells:agc-rust-shard-2
ai-game-creator-shell-rust-shard-3:
name: AI game creator shell Rust shard 3/4
ai-game-creator-shell-rust-lane-2:
name: AI game creator shell Rust lane 2/2
runs-on: genarrative-ci
steps:
- name: Checkout full history from Gitea
@@ -153,36 +121,6 @@ jobs:
- name: Run AI game creator shell Rust shard 3/4
run: npm run check:native-shells:agc-rust-shard-3
ai-game-creator-shell-rust-shard-4:
name: AI game creator shell Rust shard 4/4
runs-on: genarrative-ci
steps:
- name: Checkout full history from Gitea
env:
GENARRATIVE_GITEA_FETCH_DEPTH: '0'
GENARRATIVE_GITEA_TOKEN: ${{ github.token }}
run: genarrative-gitea-checkout
- name: Validate preinstalled CI job image and sandbox
run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh
- name: Prepare AI game creator shell Rust dependencies
shell: bash
run: |
set -euo pipefail
for attempt in $(seq 1 5); do
if cargo fetch --locked \
--target x86_64-unknown-linux-gnu \
--manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml; then
break
fi
if [[ "${attempt}" -eq 5 ]]; then
echo 'AI game creator shell Cargo dependency fetch failed after 5 attempts.' >&2
exit 1
fi
sleep $((attempt * 2))
done
- name: Run AI game creator shell Rust shard 4/4
run: npm run check:native-shells:agc-rust-shard-4
@@ -221,8 +159,7 @@ jobs:
- name: Run AI game creator shell agent-run smoke
run: npm run check:native-shells:agc-rust-smoke
# AGC 壳依赖的共享 / 平台 crate 测试用的是 server-rs workspace 与两个无锁独立 crate
# 的 manifest,属另一套依赖图,因此单独一个 job 预热、单独跑。
# AGC 壳依赖的共享 / 平台和编辑器插件 crate 各自预热独立 manifest,再运行对应测试。
ai-game-creator-shell-rust-crates:
name: AI game creator shell Rust crates
runs-on: genarrative-ci
@@ -263,13 +200,15 @@ jobs:
# `cargo test --manifest-path` 单独跑这两个 crate。不在这里预热的话,这两条测试
# 会在测试阶段自己 `Updating crates.io index`crates.io 一抖动整条 job 就红
# (见 #327 / PR #316 run 1950)。
# 两个 crate 都没有提交 Cargo.lock,所以这里只能做不带锁标志的 fetch:
# Cocos 插件也使用独立且未提交的锁文件,一并预热。
# 这些 crate 都没有提交 Cargo.lock,所以这里只能做不带锁标志的 fetch:
# 加锁标志会因为缺少锁文件直接失败。生成的 Cargo.lock 落在两个 crate 目录内,
# 已被各自的 .gitignore 忽略,只留在容器里;随后的测试阶段因此能用锁定版本
# 解析,不再触碰 registry index。
for manifest_path in \
server-rs/crates/agent-runtime-core/Cargo.toml \
server-rs/crates/agent-runtime-orchestration/Cargo.toml; do
server-rs/crates/agent-runtime-orchestration/Cargo.toml \
plugins/agc-cocos-editor/native/cocos-editor-bridge/Cargo.toml; do
for attempt in $(seq 1 5); do
if cargo fetch \
--target x86_64-unknown-linux-gnu \
@@ -284,6 +223,40 @@ jobs:
done
done
- name: Prepare Unity plugin Rust dependencies
shell: bash
run: |
set -euo pipefail
for attempt in $(seq 1 5); do
if cargo fetch --locked \
--target x86_64-unknown-linux-gnu \
--manifest-path plugins/agc-unity-editor/native/unity-editor-bridge/Cargo.toml; then
break
fi
if [[ "${attempt}" -eq 5 ]]; then
echo 'Unity plugin Cargo dependency fetch failed after 5 attempts.' >&2
exit 1
fi
sleep $((attempt * 2))
done
- name: Prepare Godot plugin Rust dependencies
shell: bash
run: |
set -euo pipefail
for attempt in $(seq 1 5); do
if cargo fetch --locked \
--target x86_64-unknown-linux-gnu \
--manifest-path plugins/agc-godot-editor/native/godot-editor-bridge/Cargo.toml; then
break
fi
if [[ "${attempt}" -eq 5 ]]; then
echo 'Godot plugin Cargo dependency fetch failed after 5 attempts.' >&2
exit 1
fi
sleep $((attempt * 2))
done
- name: Run AI game creator shell shared crate gates
run: npm run check:native-shells:agc-rust-crates
+6
View File
@@ -50,7 +50,13 @@ temp*build*/
/apps/ai-game-creator-shell/src-tauri/resources/codex/mac-native/darwin-arm64/
/apps/ai-game-creator-shell/src-tauri/resources/codex/mac-native/darwin-x64/
/plugins/agc-cocos-editor/native/payload/
/plugins/agc-unity-editor/dotnet/**/bin/
/plugins/agc-unity-editor/dotnet/**/obj/
/plugins/agc-unity-editor/dotnet/publish/
/plugins/agc-unity-editor/dotnet/native-build/
/apps/ai-game-creator-shell/logs/
/apps/ai-game-creator-shell/src-tauri/resources/node-runtime/
/apps/ai-game-creator-shell/src-tauri/resources/node-runtime-staging-*/
/apps/ai-game-creator-shell/.llm-drafts/
/apps/ai-game-creator-shell/game-creator.config.local.json
/apps/mobile-shell/.expo/
+10 -1
View File
@@ -1,5 +1,14 @@
{
"singleQuote": true,
"semi": true,
"trailingComma": "all"
"trailingComma": "all",
"overrides": [
{
"files": "packages/shared/src/contracts/generated/**/*.ts",
"options": {
"printWidth": 1000,
"singleQuote": false
}
}
]
}
+16 -19
View File
@@ -4,12 +4,13 @@
- 本文件只保留 Agent 进入仓库后必须立即遵守的最高优先级规则;完整执行细则见 [`docs/【协作规范】Agent工作入口与执行准则-2026-06-22.md`](docs/【协作规范】Agent工作入口与执行准则-2026-06-22.md)。
- 团队级长期项目记忆位于 [`docs/project-memory/`](docs/project-memory/),供 3 名开发人员和各自本地 Agent 通过 Git 同步。
- [`.codex/`](.codex/) 保存仓库级 Codex 工具资源,例如 skills、plugins、hooks 和配置模板;长期项目知识不要写入 `.codex/`
- [`.codex/`](.codex/) 保存仓库级 Codex 工具资源,例如 skills、plugins、hooks 和配置模板;长期项目知识写入 `docs/``docs/project-memory/`
-`docs/project-memory/shared-memory/` 与当前代码或最新 `docs/` 冲突,以代码和最新 `docs/` 为准,并同步修正过期共享记忆。
## 开始任务前
- 先写清一句话交付结果、验收判据和不做项,再按“必须项 / 风险项 / 可选项”排序;优先完成修改、定向验证和边界检查组成的最小闭环。设置时间盒和检查点,新增发现只有在影响交付判据时才扩大范围,否则记录为后续事项;工具探测、历史整理或验证便利不能自行改变任务目标
- 先写清一句话交付结果、验收判据和修改范围,再按“必须项 / 风险项 / 可选项”排序;优先完成修改、定向验证和边界检查组成的最小闭环。设置时间盒和检查点,新增发现只有在影响交付判据时才扩大范围,其余记录为后续事项。
- Agent 可见内容直接描述当前任务、输入和成功条件,细节按调用需要提供。
- 简单自包含任务可以直接执行;复杂开发、跨模块修改、后端 / UI / 文档体系调整前,按顺序读取:
1. 本文件。
2. [`docs/【协作规范】Agent工作入口与执行准则-2026-06-22.md`](docs/【协作规范】Agent工作入口与执行准则-2026-06-22.md)。
@@ -23,24 +24,20 @@
- 禁止提交个人 `~/.codex` 配置、`.env`、API Key、Token、Cookie、会话记录、认证文件、本地私密路径、构建产物、日志、缓存和数据库 dump。
- 不要在 `.gitignore` 中新增 `.env.local`
- 不要擅自把现有中文文案、注释、剧情文档改写成英文;看到中文乱码时先确认真实编码,不要沿用乱码或用英文替换
- 修改包含中文的文件时优先局部补丁,避免整文件重写;修改后优先运行仓库编码检查。
- 后续新增 Markdown 文档文件名必须以分类标签开头,格式为 `【标签名】中文标题-日期.md`;历史文档不要求批量重命名,除非本次任务明确涉及
- 现有中文文案、注释、剧情文档保持中文;看到乱码时先确认真实编码并恢复正确中文
- 修改包含中文的文件时优先局部补丁;修改后优先运行仓库编码检查。
- 新增 Markdown 文档文件名必须以分类标签开头,格式为 `【标签名】中文标题-日期.md`
- 工程修改要同步更新对应 `docs/` 文档;产生长期有效的架构约定、接口变化、排障经验、开发流程或协作规则时,同步更新 `docs/project-memory/shared-memory/`
- 默认保持系统简洁:优先复用、修改、扩展现有系统、页面和公共组件,不新建平行系统或平行页面
- UI 开发优先复用现有公共组件;发现跨页面或跨端重复的视觉/交互模式时,先抽取到 `packages/shared` 共享组件库并让现有页面迁移使用,禁止在业务页复制同类 UI。共享组件承载通用表现与交互,不下沉领域规则、后端副作用正式业务状态。
- 对已明确退役且不存在现役调用方、公开契约、持久化数据、活跃实例或迁移要求的对象,坚持“四不写”:
1. 不写历史兼容代码
2. 不写用于维持退役行为的防御性兼容测试
3. 不写仅说明其曾存在或已删除的墓碑注释
4. 不写仅记录其已删除的墓碑文档;直接将权威文档更新为当前状态。
- 公开 API、持久化数据、SpacetimeDB schema、跨版本重放、活跃实例和正式迁移不适用“四不写”;必要兼容应最小化、白名单化并配套契约或迁移测试,迁移完成后同步删除兼容实现与对应测试。
- UI 面板中不要默认写功能说明、规则描述或开发解释文案;移动端优先,同时保证网页端可正常显示和操作。
- 点击按钮弹出独立面板的设计,不要实现成在当前面板下面追加内容。
- 默认保持系统简洁:优先复用、修改、扩展现有系统、页面和公共组件。
- UI 开发优先复用现有公共组件;发现跨页面或跨端重复的视觉/交互模式时,先抽取到 `packages/shared` 共享组件库并让现有页面迁移使用。共享组件承载通用表现与交互,领域规则、后端副作用正式业务状态由后端负责
- 对已明确退役且不存在现役调用方、公开契约、持久化数据、活跃实例或迁移要求的对象,直接清理实现、专属测试和说明,将权威文档更新为当前状态;历史由 Git 保存。
- 公开 API、持久化数据、SpacetimeDB schema、跨版本重放、活跃实例和正式迁移按实际需求保留最小、白名单化的兼容,并配套契约或迁移测试;迁移完成后同步删除兼容实现与对应测试
- UI 面板优先呈现任务内容与操作,说明按当前操作需要提供;移动端优先,同时保证网页端可正常显示和操作
- 点击按钮弹出的独立面板使用弹窗、抽屉、popover 或页面级 portal
## 任务路由
- Issue 使用自托管 Gitea;优先用 Gitea UI/API 或 `tea` CLI,不使用 GitHub `gh` 或 GitLab `glab`,除非仓库已迁移。默认 triage 标签:`needs-triage``needs-info``ready-for-agent``ready-for-human``wontfix`
- Issue 使用自托管 Gitea;优先用 Gitea UI/API 或 `tea` CLI。默认 triage 标签:`needs-triage``needs-info``ready-for-agent``ready-for-human``wontfix`
- 需要仓库级 Codex skills/plugins 时,再读取 [`.codex/README.md`](.codex/README.md)。
- 涉及 AI 游戏创作独立 App、多智能体 Runtime、本地项目产物或本地 HTTP 预览时,先读取 [`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`](docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md)。
- 新增、补齐、迁移或重构玩法入口、玩法类型、创作工作台、生成页、结果页、发布、运行态、作品架、广场或公开 read model 前,必须读取并按 [`genarrative-play-type-integration`](.codex/skills/genarrative-play-type-integration/SKILL.md) 执行。
@@ -50,10 +47,10 @@
## 后端红线
- 后端最新技术约束以 [`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`](docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md) 为准。
- 后端路线固定为 `server-rs + Axum + SpacetimeDB`;旧 `server-node`、Express、PostgreSQL、Go 服务端、`maincloud` / `Maincloud` / `MAINCLOUD` 只作为历史残留,不作为兼容目标
- 后端路线固定为 `server-rs + Axum + SpacetimeDB`
- DDD 分层边界按总纲执行:领域规则沉到 `module-*`SpacetimeDB 表和事务编排留在 `spacetime-module`,后端访问 SpacetimeDB 统一经 `spacetime-client` facadeHTTP/SSE/BFF 留在 `api-server`,外部副作用留在 `platform-*`,前后端 DTO 留在 `shared-contracts`
- 前端只做表现、交互和临时 UI 状态,不承接正式业务真相,不绕过后端投影后端 API 直接实现业务规则
- 契约、路由、DTO 去留和 breaking change 以当前后端架构文档、`server-rs/crates/api-server/src/app.rs``shared-contracts``packages/shared` 为准;不得在前端、`api-server` 或临时兼容层中重新发明旧接口
- 前端负责表现、交互和临时 UI 状态;正式业务状态与规则以后端投影后端 API 为准
- 契约、路由、DTO 去留和 breaking change 以当前后端架构文档、`server-rs/crates/api-server/src/app.rs``shared-contracts``packages/shared` 为准。
- 凡修改 `/api/external/v1` 的路由、HTTP 方法、请求 / 响应 DTO、请求头、状态码、鉴权或异步语义,必须在同一次变更中同步更新权威契约 [`docs/openapi/genarrative-external-v1.openapi.json`](docs/openapi/genarrative-external-v1.openapi.json) 及对应契约测试;Rust 实现与 OpenAPI 未保持一致时任务不得视为完成。
- SpacetimeDB 已有表新增字段时,字段必须放在 Rust 表结构体最后,并设置明确默认值;需要删除、改名、重排或改类型时,必须先询问用户并确认迁移计划。
- 修改 SpacetimeDB schema 后必须同步 `migration.rs`、表目录和生成绑定,并运行 `npm run check:spacetime-schema`
@@ -3,12 +3,14 @@ import { afterEach, expect, test, vi } from 'vitest';
import {
createAdminAccount,
executeAdminRechargeRefund,
getAdminAgcTemplates,
getAdminFeatureGateConfig,
getAdminUserDetail,
listAdminRechargeOrders,
reconcileAdminUserConsumption,
resolveAdminRechargeRefundManualReview,
updateAdminAccount,
updateAdminAgcTemplate,
uploadAdminEditorShowcaseCampaignImage,
upsertAdminFeatureGateConfig,
upsertProfileWalletConfig,
@@ -18,6 +20,51 @@ afterEach(() => {
vi.unstubAllGlobals();
});
test('模板管理读取和更新复用认证封装,提交 revision 和封面但不提交 ZIP 或版本', async () => {
const library = { revision: 'revision-new', writable: true, templates: [] };
const fetchMock = vi.fn().mockImplementation(
async () =>
new Response(JSON.stringify({ ok: true, data: library }), {
status: 200,
}),
);
vi.stubGlobal('fetch', fetchMock);
const controller = new AbortController();
expect(await getAdminAgcTemplates('admin-token', controller.signal)).toEqual(
library,
);
const update = {
expectedRevision: 'revision-old',
title: '空白模板',
summary: '简介',
tags: ['2D'],
enabled: true,
cover: { contentType: 'image/png', dataBase64: 'aW1hZ2U=' },
};
expect(
await updateAdminAgcTemplate('admin-token', 'template/1', update),
).toEqual(library);
expect(fetchMock.mock.calls[0]).toEqual([
'/admin/api/agc-templates',
expect.objectContaining({
method: 'GET',
signal: controller.signal,
headers: expect.objectContaining({ Authorization: 'Bearer admin-token' }),
}),
]);
expect(fetchMock.mock.calls[1]).toEqual([
'/admin/api/agc-templates/template%2F1',
expect.objectContaining({
method: 'PUT',
headers: expect.objectContaining({
Authorization: 'Bearer admin-token',
'Content-Type': 'application/json',
}),
body: JSON.stringify(update),
}),
]);
});
test('后台账号创建和更新同时携带 Tab 与独立操作权限', async () => {
const fetchMock = vi.fn().mockImplementation(() =>
Promise.resolve(
+108
View File
@@ -1,5 +1,6 @@
import type {
AdminAccountListResponse,
AdminAgcTemplateLibraryResponse,
AdminConfirmEditorShowcaseCampaignImageUploadRequest,
AdminCreateAccountRequest,
AdminCreateAccountResponse,
@@ -32,6 +33,8 @@ import type {
AdminLoginResponse,
AdminMeResponse,
AdminOverviewResponse,
AdminProjectSnapshotListQuery,
AdminProjectSnapshotListResponse,
AdminRechargeOrderListQuery,
AdminRechargeOrderListResponse,
AdminRechargeRefundActionResponse,
@@ -45,6 +48,7 @@ import type {
AdminTrackingEventListResponse,
AdminUpdateAccountRequest,
AdminUpdateAccountResponse,
AdminUpdateAgcTemplateRequest,
AdminUploadedEditorShowcaseCampaignImage,
AdminUpsertEditorShowcaseCampaignRequest,
AdminUpsertFeatureGateConfigRequest,
@@ -198,6 +202,92 @@ export function listAdminAccounts(token: string) {
return request<AdminAccountListResponse>('/admin/api/accounts', { token });
}
export function listAdminProjectSnapshots(
token: string,
query: AdminProjectSnapshotListQuery = {},
signal?: AbortSignal,
) {
const params = new URLSearchParams();
if (query.cursor) params.set('cursor', query.cursor);
params.set('limit', String(query.limit ?? 20));
return request<AdminProjectSnapshotListResponse>(
`/admin/api/project-snapshots?${params.toString()}`,
{ token, signal },
);
}
export async function downloadAdminProjectSnapshot(
token: string,
userId: string,
projectId: string,
signal?: AbortSignal,
) {
const path = `/admin/api/project-snapshots/${encodeURIComponent(userId)}/${encodeURIComponent(projectId)}/download`;
const response = await fetch(buildRequestUrl(path), {
headers: {
Authorization: `Bearer ${token.trim()}`,
Accept: 'application/zip',
[API_RESPONSE_ENVELOPE_HEADER]: 'v1',
},
signal,
});
if (!response.ok) {
const responseText = await response.text();
throw buildAdminApiError(
response,
parseJsonResponse(responseText),
responseText,
);
}
const contentType = response.headers
.get('content-type')
?.split(';')[0]
?.trim()
.toLowerCase();
if (contentType !== 'application/zip') {
await response.body?.cancel();
throw new AdminApiError({
message: '下载失败:服务端未返回 ZIP 工程文件',
status: response.status,
code: 'INVALID_PROJECT_ARCHIVE_RESPONSE',
});
}
return {
blob: await response.blob(),
filename: projectArchiveFilename(
response.headers.get('content-disposition'),
),
};
}
function projectArchiveFilename(contentDisposition: string | null): string {
const extended = contentDisposition?.match(
/(?:^|;)\s*filename\*=UTF-8'[^']*'([^;]+)/i,
);
const ordinary = contentDisposition?.match(
/(?:^|;)\s*filename=(?:"((?:[^"\\]|\\.)*)"|([^;]+))/i,
);
let filename =
ordinary?.[1]?.replace(/\\(.)/g, '$1') ?? ordinary?.[2]?.trim() ?? '';
if (extended?.[1]) {
try {
filename = decodeURIComponent(extended[1].trim());
} catch {
// 非法扩展编码继续使用普通文件名。
}
}
const safeName = Array.from(filename, (character) => {
const code = character.charCodeAt(0);
return code < 32 || code === 127 ? '_' : character;
})
.join('')
.replace(/[<>:"/\\|?*]/g, '_')
.trim()
.replace(/[. ]+$/, '');
if (!safeName || safeName.length > 240) return 'project.zip';
return /\.zip$/i.test(safeName) ? safeName : `${safeName}.zip`;
}
export function createAdminAccount(
token: string,
payload: AdminCreateAccountRequest,
@@ -1088,3 +1178,21 @@ export function saveAgcModelCatalog(
{ token, method: 'PUT', body },
);
}
export function getAdminAgcTemplates(token: string, signal?: AbortSignal) {
return request<AdminAgcTemplateLibraryResponse>('/admin/api/agc-templates', {
token,
signal,
});
}
export function updateAdminAgcTemplate(
token: string,
id: string,
body: AdminUpdateAgcTemplateRequest,
) {
return request<AdminAgcTemplateLibraryResponse>(
`/admin/api/agc-templates/${encodeURIComponent(id)}`,
{ token, method: 'PUT', body },
);
}
+55
View File
@@ -96,6 +96,27 @@ export interface AdminMeResponse {
admin: AdminSessionPayload;
}
export interface AdminProjectSnapshotEntry {
userId: string;
projectId: string;
projectName: string | null;
syncRevision: number;
syncedAtMs: number;
fileCount: number;
totalBytes: number;
status: 'ready' | 'partial' | 'unverified';
}
export interface AdminProjectSnapshotListQuery {
cursor?: string | null;
limit?: number;
}
export interface AdminProjectSnapshotListResponse {
items: AdminProjectSnapshotEntry[];
nextCursor: string | null;
}
export interface AdminErrorReportEntry {
batchId: string;
eventCount: number;
@@ -123,6 +144,8 @@ export interface AdminErrorReportListResponse {
}
export interface AdminErrorReportDetail extends AdminErrorReportEntry {
firstFingerprint?: string;
firstSource?: string;
note?: string;
events: Array<Record<string, unknown>>;
logNames: string[];
@@ -1010,3 +1033,35 @@ export interface AdminAgcModelCatalog {
defaultModelId: string;
models: AdminAgcModel[];
}
export interface AdminAgcTemplatePayload {
id: string;
title: string;
summary: string;
tags: string[];
runtime: string;
engine: string;
engineVersion: string;
templateVersion: string;
enabled: boolean;
coverUrl: string;
zipSizeBytes: number;
}
export interface AdminAgcTemplateLibraryResponse {
revision: string;
writable: boolean;
templates: AdminAgcTemplatePayload[];
}
export interface AdminUpdateAgcTemplateRequest {
expectedRevision: string;
title: string;
summary: string;
tags: string[];
enabled: boolean;
cover?: {
contentType: string;
dataBase64: string;
};
}
@@ -0,0 +1,128 @@
import { afterEach, expect, test, vi } from 'vitest';
import {
downloadAdminProjectSnapshot,
listAdminProjectSnapshots,
} from './adminApiClient';
afterEach(() => {
vi.unstubAllGlobals();
});
test('项目列表携带分页与后台授权,解析标准响应', async () => {
const payload = { items: [], nextCursor: 'next' };
const fetchMock = vi
.fn()
.mockResolvedValue(
new Response(JSON.stringify({ ok: true, data: payload })),
);
vi.stubGlobal('fetch', fetchMock);
const controller = new AbortController();
expect(
await listAdminProjectSnapshots(
'admin-token',
{ cursor: 'user/a+项目', limit: 20 },
controller.signal,
),
).toEqual(payload);
expect(fetchMock).toHaveBeenCalledWith(
'/admin/api/project-snapshots?cursor=user%2Fa%2B%E9%A1%B9%E7%9B%AE&limit=20',
expect.objectContaining({
headers: expect.objectContaining({ Authorization: 'Bearer admin-token' }),
signal: controller.signal,
}),
);
});
test('ZIP 下载以授权请求读取并优先保留中文附件名', async () => {
const fetchMock = vi.fn().mockResolvedValue(
new Response('PK\u0003\u0004', {
headers: {
'content-type': 'application/zip',
'content-disposition':
"attachment; filename=project.zip; filename*=UTF-8''%E4%B8%89%E6%B6%88-r2.zip",
},
}),
);
vi.stubGlobal('fetch', fetchMock);
const controller = new AbortController();
const archive = await downloadAdminProjectSnapshot(
'admin-token',
'user/a',
'project/b',
controller.signal,
);
expect(archive.filename).toBe('三消-r2.zip');
expect(archive.blob.type).toBe('application/zip');
expect(fetchMock).toHaveBeenCalledWith(
'/admin/api/project-snapshots/user%2Fa/project%2Fb/download',
expect.objectContaining({
headers: expect.objectContaining({
Authorization: 'Bearer admin-token',
Accept: 'application/zip',
}),
signal: controller.signal,
}),
);
});
test.each([
['attachment; filename="工程.zip"; filename*=UTF-8\'\'%broken', '工程.zip'],
['attachment; filename="../secret.zip"', '.._secret.zip'],
["attachment; filename*=UTF-8''unsafe%00%1F%7F.zip", 'unsafe___.zip'],
[null, 'project.zip'],
])('ZIP 附件名兼容安全回退 %s', async (header, expected) => {
const headers: Record<string, string> = { 'content-type': 'application/zip' };
// Response 的 Headers 只接受 Latin-1;真实 UTF-8 文件名使用 filename*。
if (header)
headers['content-disposition'] = header.replace('工程', 'project');
vi.stubGlobal(
'fetch',
vi.fn().mockResolvedValue(new Response('PK', { headers })),
);
expect(
(await downloadAdminProjectSnapshot('token', 'user', 'project')).filename,
).toBe(expected.replace('工程', 'project'));
});
test.each([401, 403, 409, 500])(
'下载 HTTP %s 保留后台错误,不返回 ZIP',
async (status) => {
vi.stubGlobal(
'fetch',
vi.fn().mockResolvedValue(
new Response(
JSON.stringify({
ok: false,
error: { code: 'SNAPSHOT_FAILURE', message: '工程尚未同步完成' },
}),
{
status,
headers: { 'content-type': 'application/json' },
},
),
),
);
await expect(
downloadAdminProjectSnapshot('token', 'user', 'project'),
).rejects.toMatchObject({
status,
code: 'SNAPSHOT_FAILURE',
message: '工程尚未同步完成',
});
},
);
test('200 JSON 或 HTML 不能被保存为成功 ZIP', async () => {
vi.stubGlobal(
'fetch',
vi.fn().mockResolvedValue(
new Response('{"ok":false}', {
headers: { 'content-type': 'application/json' },
}),
),
);
await expect(
downloadAdminProjectSnapshot('token', 'user', 'project'),
).rejects.toMatchObject({ code: 'INVALID_PROJECT_ARCHIVE_RESPONSE' });
});
+14
View File
@@ -19,6 +19,7 @@ import {
} from '../auth/adminAuthStore';
import { AdminAccountsPage } from '../pages/AdminAccountsPage';
import { AdminAgcModelsPage } from '../pages/AdminAgcModelsPage';
import { AdminAgcTemplatesPage } from '../pages/AdminAgcTemplatesPage';
import { AdminDashboardPage } from '../pages/AdminDashboardPage';
import { AdminDatabaseTablesPage } from '../pages/AdminDatabaseTablesPage';
import { AdminDebugHttpPage } from '../pages/AdminDebugHttpPage';
@@ -31,6 +32,7 @@ import { AdminInviteCodePage } from '../pages/AdminInviteCodePage';
import { AdminLoginPage } from '../pages/AdminLoginPage';
import { AdminOverviewPage } from '../pages/AdminOverviewPage';
import { AdminProfileWalletConfigPage } from '../pages/AdminProfileWalletConfigPage';
import { AdminProjectSnapshotsPage } from '../pages/AdminProjectSnapshotsPage';
import { AdminRechargeOrderPage } from '../pages/AdminRechargeOrderPage';
import { AdminRechargeProductPage } from '../pages/AdminRechargeProductPage';
import { AdminRedeemCodePage } from '../pages/AdminRedeemCodePage';
@@ -293,6 +295,12 @@ export function AdminApp() {
{activeRouteId === 'agc-models' ? (
<AdminAgcModelsPage token={token} onUnauthorized={handleUnauthorized} />
) : null}
{activeRouteId === 'agc-templates' ? (
<AdminAgcTemplatesPage
token={token}
onUnauthorized={handleUnauthorized}
/>
) : null}
{activeRouteId === 'editor-showcase' ? (
<AdminEditorShowcaseReviewPage
token={token}
@@ -308,6 +316,12 @@ export function AdminApp() {
{activeRouteId === 'accounts' ? (
<AdminAccountsPage token={token} onUnauthorized={handleUnauthorized} />
) : null}
{activeRouteId === 'project-snapshots' ? (
<AdminProjectSnapshotsPage
token={token}
onUnauthorized={handleUnauthorized}
/>
) : null}
</AdminShell>
);
}
+3
View File
@@ -4,6 +4,7 @@ import {
Bug,
Coins,
Database,
FolderArchive,
GitBranch,
Images,
LayoutDashboard,
@@ -49,8 +50,10 @@ const routeIcons = {
'editor-generation-pricing': Coins,
'editor-showcase': Star,
'editor-assets': Images,
'project-snapshots': FolderArchive,
accounts: Users,
'agc-models': ListChecks,
'agc-templates': Images,
} satisfies Record<AdminRouteId, typeof LayoutDashboard>;
export function AdminShell({
@@ -122,3 +122,55 @@ test('零权限 member 不回落到 Dashboard', () => {
expect(routes).toEqual([]);
expect(resolveAccessibleAdminRoute('#dashboard', routes)).toBeNull();
});
test('项目工程入口对 owner 与已授权 member 开放且可分配权限', () => {
const route = {
id: 'project-snapshots',
label: '项目工程',
hash: '#project-snapshots',
};
expect(adminRoutes.filter((item) => !item.ownerOnly)).toContainEqual(route);
expect(resolveAdminRoute('#project-snapshots')).toBe('project-snapshots');
expect(
getAccessibleAdminRoutes({ accountRole: 'owner', tabPermissions: [] }),
).toContainEqual(route);
expect(
getAccessibleAdminRoutes({
accountRole: 'member',
tabPermissions: ['project-snapshots'],
}),
).toEqual([route]);
expect(
getAccessibleAdminRoutes({
accountRole: 'member',
tabPermissions: ['tracking'],
}),
).not.toContainEqual(route);
});
test('模板管理只对 owner 或具有 agc-templates 权限的 member 可见', () => {
expect(adminRoutes).toContainEqual({
id: 'agc-templates',
label: '模板管理',
hash: '#agc-templates',
});
expect(resolveAdminRoute('#agc-templates')).toBe('agc-templates');
expect(routeHash('agc-templates')).toBe('#agc-templates');
expect(
getAccessibleAdminRoutes({ accountRole: 'owner', tabPermissions: [] }).some(
(route) => route.id === 'agc-templates',
),
).toBe(true);
expect(
getAccessibleAdminRoutes({
accountRole: 'member',
tabPermissions: ['agc-templates'],
}).map((route) => route.id),
).toEqual(['agc-templates']);
expect(
getAccessibleAdminRoutes({
accountRole: 'member',
tabPermissions: ['editor-assets'],
}).some((route) => route.id === 'agc-templates'),
).toBe(false);
});
+4
View File
@@ -16,7 +16,9 @@ export type AdminRouteId =
| 'editor-generation-pricing'
| 'editor-showcase'
| 'editor-assets'
| 'project-snapshots'
| 'agc-models'
| 'agc-templates'
| 'accounts';
export type AdminTabPermission = Exclude<
@@ -52,8 +54,10 @@ export const adminRoutes: AdminRouteDefinition[] = [
hash: '#editor-generation-pricing',
},
{ id: 'agc-models', label: 'AGC 模型', hash: '#agc-models', ownerOnly: true },
{ id: 'agc-templates', label: '模板管理', hash: '#agc-templates' },
{ id: 'editor-showcase', label: '精选审核', hash: '#editor-showcase' },
{ id: 'editor-assets', label: '素材查询', hash: '#editor-assets' },
{ id: 'project-snapshots', label: '项目工程', hash: '#project-snapshots' },
{ id: 'accounts', label: '账号管理', hash: '#accounts', ownerOnly: true },
];
+1
View File
@@ -1,3 +1,4 @@
import '@genarrative/shared/styles.css';
import './styles/admin.css';
import { StrictMode } from 'react';
@@ -0,0 +1,498 @@
// @vitest-environment jsdom
import {
act,
cleanup,
fireEvent,
render,
screen,
waitFor,
within,
} from '@testing-library/react';
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
import {
AdminApiError,
getAdminAgcTemplates,
updateAdminAgcTemplate,
} from '../api/adminApiClient';
import type {
AdminAgcTemplateLibraryResponse,
AdminAgcTemplatePayload,
} from '../api/adminApiTypes';
import { AdminAgcTemplatesPage } from './AdminAgcTemplatesPage';
vi.mock('../api/adminApiClient', async (importOriginal) => ({
...(await importOriginal<typeof import('../api/adminApiClient')>()),
getAdminAgcTemplates: vi.fn(),
updateAdminAgcTemplate: vi.fn(),
}));
const template: AdminAgcTemplatePayload = {
id: 'cocos-empty-2d',
title: '空白 2D',
summary: '二维项目',
tags: ['2D', '入门'],
runtime: 'cocos',
engine: 'Cocos Creator',
engineVersion: '3.8.8',
templateVersion: '1',
enabled: true,
coverUrl: 'https://example.test/cover.png',
zipSizeBytes: 2048,
};
const library: AdminAgcTemplateLibraryResponse = {
revision: 'rev-1',
writable: true,
templates: [
template,
{
...template,
id: 'godot-starter',
title: 'Godot 起步',
runtime: 'godot',
engine: 'Godot',
engineVersion: '4',
tags: ['3D'],
enabled: false,
},
],
};
beforeEach(() => {
vi.mocked(getAdminAgcTemplates)
.mockReset()
.mockImplementation(async () => structuredClone(library));
vi.mocked(updateAdminAgcTemplate)
.mockReset()
.mockImplementation(async (_token, id, update) => ({
...structuredClone(library),
revision: 'rev-2',
templates: library.templates.map((entry) =>
entry.id === id
? {
...entry,
title: update.title,
summary: update.summary,
tags: update.tags,
enabled: update.enabled,
}
: entry,
),
}));
vi.stubGlobal(
'URL',
Object.assign(class extends URL {}, {
createObjectURL: vi.fn(() => 'blob:template-cover'),
revokeObjectURL: vi.fn(),
}),
);
});
afterEach(() => {
cleanup();
vi.unstubAllGlobals();
vi.restoreAllMocks();
});
async function openEditor() {
fireEvent.click(await screen.findByRole('button', { name: '编辑 空白 2D' }));
return screen.getByRole('dialog', { name: '编辑模板' });
}
async function confirmWrite() {
const confirmation = await screen.findByRole('dialog', { name: '确认操作' });
fireEvent.click(within(confirmation).getByRole('button', { name: '确认' }));
}
function deferred<T>() {
let resolve!: (value: T) => void;
let reject!: (reason: unknown) => void;
const promise = new Promise<T>((next, fail) => {
resolve = next;
reject = fail;
});
return { promise, resolve, reject };
}
test('按名称、ID、标签、运行时和上下架状态筛选,展示版本与封面', async () => {
render(<AdminAgcTemplatesPage token="token" onUnauthorized={vi.fn()} />);
await screen.findByText('空白 2D');
expect(screen.getByText('Cocos Creator 3.8.8')).not.toBeNull();
expect(screen.getAllByText('模板 1')).toHaveLength(2);
expect(screen.getAllByText('2.0 KiB')).toHaveLength(2);
expect(
screen.getByRole('img', { name: '空白 2D封面' }).getAttribute('src'),
).toBe(template.coverUrl);
for (const value of ['空白', 'cocos-empty-2d', '入门']) {
fireEvent.change(screen.getByLabelText('搜索模板'), { target: { value } });
expect(screen.getByText('空白 2D')).not.toBeNull();
expect(screen.queryByText('Godot 起步')).toBeNull();
}
fireEvent.change(screen.getByLabelText('搜索模板'), {
target: { value: '' },
});
fireEvent.change(screen.getByLabelText('运行时'), {
target: { value: 'godot' },
});
expect(screen.queryByText('空白 2D')).toBeNull();
fireEvent.change(screen.getByLabelText('上架状态'), {
target: { value: 'enabled' },
});
expect(screen.getByText('没有符合筛选条件的模板')).not.toBeNull();
});
test('独立弹窗保存经写确认,只提交展示字段和冻结 revision', async () => {
render(<AdminAgcTemplatesPage token="token" onUnauthorized={vi.fn()} />);
const editor = await openEditor();
expect(editor.closest('.admin-agc-templates')).toBeNull();
fireEvent.change(within(editor).getByLabelText('名称'), {
target: { value: ' 新名称 ' },
});
fireEvent.change(within(editor).getByLabelText('简介'), {
target: { value: '新简介' },
});
fireEvent.change(within(editor).getByLabelText('标签'), {
target: { value: '2D, 新手,2D' },
});
fireEvent.click(within(editor).getByRole('button', { name: '保存' }));
expect(updateAdminAgcTemplate).not.toHaveBeenCalled();
await confirmWrite();
await waitFor(() =>
expect(updateAdminAgcTemplate).toHaveBeenCalledWith('token', template.id, {
expectedRevision: 'rev-1',
title: '新名称',
summary: '新简介',
tags: ['2D', '新手'],
enabled: true,
}),
);
await waitFor(() =>
expect(screen.queryByRole('dialog', { name: '编辑模板' })).toBeNull(),
);
expect(screen.getByText('新名称')).not.toBeNull();
expect(screen.getByText('已保存')).not.toBeNull();
});
test('上下架复用确认且只用服务端结果更新列表', async () => {
render(<AdminAgcTemplatesPage token="token" onUnauthorized={vi.fn()} />);
fireEvent.click(await screen.findByRole('button', { name: '下架 空白 2D' }));
await confirmWrite();
await waitFor(() =>
expect(updateAdminAgcTemplate).toHaveBeenCalledWith('token', template.id, {
expectedRevision: 'rev-1',
title: template.title,
summary: template.summary,
tags: template.tags,
enabled: false,
}),
);
expect(
await screen.findByRole('button', { name: '上架 空白 2D' }),
).not.toBeNull();
});
test('只读模式禁用字段、保存与上下架', async () => {
vi.mocked(getAdminAgcTemplates).mockResolvedValue({
...library,
writable: false,
});
render(<AdminAgcTemplatesPage token="token" onUnauthorized={vi.fn()} />);
expect(
await screen.findByText('当前为只读模式,无法保存或上下架'),
).not.toBeNull();
expect(
(screen.getByRole('button', { name: '下架 空白 2D' }) as HTMLButtonElement)
.disabled,
).toBe(true);
const editor = await openEditor();
expect(
(within(editor).getByLabelText('名称') as HTMLInputElement).disabled,
).toBe(true);
expect(
(within(editor).getByRole('button', { name: '保存' }) as HTMLButtonElement)
.disabled,
).toBe(true);
expect(updateAdminAgcTemplate).not.toHaveBeenCalled();
});
test('409 保留草稿,显式刷新后重新编辑才采用新 revision,不自动重试', async () => {
vi.mocked(updateAdminAgcTemplate).mockRejectedValueOnce(
new AdminApiError({ message: '模板库已被修改', status: 409 }),
);
render(<AdminAgcTemplatesPage token="token" onUnauthorized={vi.fn()} />);
let editor = await openEditor();
fireEvent.change(within(editor).getByLabelText('名称'), {
target: { value: '未保存草稿' },
});
fireEvent.click(within(editor).getByRole('button', { name: '保存' }));
await confirmWrite();
await screen.findByText('模板库已被修改');
expect((screen.getByLabelText('名称') as HTMLInputElement).value).toBe(
'未保存草稿',
);
expect(updateAdminAgcTemplate).toHaveBeenCalledTimes(1);
expect(
(screen.getByRole('button', { name: '保存' }) as HTMLButtonElement)
.disabled,
).toBe(true);
vi.mocked(getAdminAgcTemplates).mockResolvedValueOnce({
...library,
revision: 'rev-3',
templates: [{ ...template, title: '最新名称' }],
});
fireEvent.click(screen.getByRole('button', { name: '刷新后重新编辑' }));
await screen.findByDisplayValue('最新名称');
editor = screen.getByRole('dialog', { name: '编辑模板' });
fireEvent.change(within(editor).getByLabelText('名称'), {
target: { value: '重新编辑' },
});
fireEvent.click(within(editor).getByRole('button', { name: '保存' }));
await confirmWrite();
await waitFor(() =>
expect(updateAdminAgcTemplate).toHaveBeenLastCalledWith(
'token',
template.id,
expect.objectContaining({ expectedRevision: 'rev-3', title: '重新编辑' }),
),
);
});
test('防止重复确认和保存中重复提交', async () => {
const pending = deferred<AdminAgcTemplateLibraryResponse>();
vi.mocked(updateAdminAgcTemplate).mockReturnValue(pending.promise);
render(<AdminAgcTemplatesPage token="token" onUnauthorized={vi.fn()} />);
const editor = await openEditor();
const form = within(editor).getByLabelText('名称').closest('form')!;
fireEvent.submit(form);
fireEvent.submit(form);
expect(screen.getAllByRole('dialog', { name: '确认操作' })).toHaveLength(1);
await confirmWrite();
await waitFor(() => expect(updateAdminAgcTemplate).toHaveBeenCalledTimes(1));
fireEvent.submit(form);
expect(updateAdminAgcTemplate).toHaveBeenCalledTimes(1);
await act(async () => pending.resolve(library));
});
test.each([409, 503])(
'滚到表单底部后出现 HTTP %s 写入错误,聚焦并仅滚动弹窗到提示区',
async (status) => {
vi.mocked(updateAdminAgcTemplate).mockRejectedValueOnce(
new AdminApiError({ message: '本次保存失败', status }),
);
render(
<div data-testid="background-page">
<AdminAgcTemplatesPage token="token" onUnauthorized={vi.fn()} />
</div>,
);
const background = screen.getByTestId('background-page');
background.scrollTop = 240;
const editor = await openEditor();
const viewport = editor.querySelector<HTMLElement>(
'.genarrative-ui-modal__body',
)!;
viewport.scrollTop = 400;
const bounds = (top: number, bottom: number) =>
({
top,
bottom,
height: bottom - top,
left: 0,
right: 300,
width: 300,
x: 0,
y: top,
toJSON: () => ({}),
}) as DOMRect;
vi.spyOn(viewport, 'getBoundingClientRect').mockReturnValue(
bounds(100, 450),
);
const originalBounds = HTMLElement.prototype.getBoundingClientRect;
vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(
function (this: HTMLElement) {
return this.classList.contains('admin-agc-template-feedback')
? bounds(-280, -120)
: originalBounds.call(this);
},
);
const windowScroll = vi
.spyOn(window, 'scrollTo')
.mockImplementation(() => {});
const focus = vi.spyOn(HTMLElement.prototype, 'focus');
fireEvent.click(within(editor).getByRole('button', { name: '保存' }));
await confirmWrite();
const message = await screen.findByRole('alert');
const feedback = message.parentElement!;
expect(document.activeElement).toBe(feedback);
expect(feedback.tabIndex).toBe(-1);
expect(focus).toHaveBeenCalledWith({ preventScroll: true });
expect(viewport.scrollTop).toBe(20);
expect(background.scrollTop).toBe(240);
expect(windowScroll).not.toHaveBeenCalled();
if (status === 409) {
expect(
within(feedback).getByRole('button', { name: '刷新后重新编辑' }),
).not.toBeNull();
}
},
);
test('token 切换后忽略旧列表和未确认写操作', async () => {
const pending = deferred<AdminAgcTemplateLibraryResponse>();
vi.mocked(getAdminAgcTemplates).mockReturnValueOnce(pending.promise);
const props = { onUnauthorized: vi.fn() };
const view = render(<AdminAgcTemplatesPage token="old" {...props} />);
view.rerender(<AdminAgcTemplatesPage token="new" {...props} />);
await screen.findByText('空白 2D');
await act(async () =>
pending.resolve({
...library,
templates: [{ ...template, title: '过期列表' }],
}),
);
expect(screen.queryByText('过期列表')).toBeNull();
const editor = await openEditor();
fireEvent.click(within(editor).getByRole('button', { name: '保存' }));
view.rerender(<AdminAgcTemplatesPage token="third" {...props} />);
await screen.findByText('空白 2D');
expect(screen.queryByRole('dialog')).toBeNull();
expect(updateAdminAgcTemplate).not.toHaveBeenCalled();
});
test.each(['success', 'unauthorized'])(
'token 切换后忽略旧保存 %s,不污染新会话',
async (result) => {
const pending = deferred<AdminAgcTemplateLibraryResponse>();
vi.mocked(updateAdminAgcTemplate).mockReturnValueOnce(pending.promise);
const onUnauthorized = vi.fn();
const view = render(
<AdminAgcTemplatesPage token="old" onUnauthorized={onUnauthorized} />,
);
const editor = await openEditor();
fireEvent.click(within(editor).getByRole('button', { name: '保存' }));
await confirmWrite();
await waitFor(() =>
expect(updateAdminAgcTemplate).toHaveBeenCalledTimes(1),
);
view.rerender(
<AdminAgcTemplatesPage token="new" onUnauthorized={onUnauthorized} />,
);
await screen.findByText('空白 2D');
await act(async () => {
if (result === 'success') {
pending.resolve({
...library,
templates: [{ ...template, title: '旧保存结果' }],
});
} else {
pending.reject(
new AdminApiError({ status: 401, message: '旧会话失效' }),
);
}
});
expect(onUnauthorized).not.toHaveBeenCalled();
expect(screen.queryByText('旧会话失效')).toBeNull();
expect(screen.queryByText('旧保存结果')).toBeNull();
expect(screen.queryByText('已保存')).toBeNull();
},
);
test('封面在草稿中转 base64,保存携带,取消释放预览 URL 且不串到下一条', async () => {
render(<AdminAgcTemplatesPage token="token" onUnauthorized={vi.fn()} />);
let editor = await openEditor();
const file = new File(['cover'], 'cover.png', { type: 'image/png' });
fireEvent.change(within(editor).getByLabelText('更换封面'), {
target: { files: [file] },
});
await waitFor(() => expect(screen.queryByText('正在读取封面')).toBeNull());
fireEvent.click(within(editor).getByRole('button', { name: '保存' }));
await confirmWrite();
await waitFor(() =>
expect(updateAdminAgcTemplate).toHaveBeenCalledWith(
'token',
template.id,
expect.objectContaining({
cover: { contentType: 'image/png', dataBase64: 'Y292ZXI=' },
}),
),
);
await waitFor(() =>
expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:template-cover'),
);
editor = await openEditor();
fireEvent.change(within(editor).getByLabelText('更换封面'), {
target: { files: [file] },
});
fireEvent.click(within(editor).getByRole('button', { name: '取消' }));
fireEvent.click(screen.getByRole('button', { name: '编辑 Godot 起步' }));
expect(
screen.getByRole('img', { name: '模板封面预览' }).getAttribute('src'),
).toBe(template.coverUrl);
expect(URL.revokeObjectURL).toHaveBeenCalledTimes(2);
});
test.each([
['bad.svg', 'image/svg+xml', 1, '封面仅支持 PNG、JPEG 或 WebP'],
[
'large.png',
'image/png',
5 * 1024 * 1024 + 1,
'封面文件须大于 0 且不超过 5 MiB',
],
])('拒绝无效封面 %s,不上传', async (name, type, size, message) => {
render(<AdminAgcTemplatesPage token="token" onUnauthorized={vi.fn()} />);
const editor = await openEditor();
const file = new File(['x'], name, { type });
Object.defineProperty(file, 'size', { value: size });
fireEvent.change(within(editor).getByLabelText('更换封面'), {
target: { files: [file] },
});
expect(screen.getByText(message)).not.toBeNull();
expect(
(screen.getByRole('button', { name: '保存' }) as HTMLButtonElement)
.disabled,
).toBe(true);
expect(URL.createObjectURL).not.toHaveBeenCalled();
expect(updateAdminAgcTemplate).not.toHaveBeenCalled();
});
test('名称和标签校验失败时不进入写确认', async () => {
render(<AdminAgcTemplatesPage token="token" onUnauthorized={vi.fn()} />);
const editor = await openEditor();
fireEvent.change(within(editor).getByLabelText('名称'), {
target: { value: ' ' },
});
fireEvent.click(within(editor).getByRole('button', { name: '保存' }));
expect(screen.getByText('名称须为 180 个字符')).not.toBeNull();
fireEvent.change(within(editor).getByLabelText('名称'), {
target: { value: '有效名称' },
});
fireEvent.change(within(editor).getByLabelText('标签'), {
target: {
value: Array.from({ length: 17 }, (_, index) => `标签${index}`).join(','),
},
});
fireEvent.click(within(editor).getByRole('button', { name: '保存' }));
expect(
screen.getByText('最多 16 个标签,每个标签不超过 32 个字符'),
).not.toBeNull();
expect(updateAdminAgcTemplate).not.toHaveBeenCalled();
});
test('读取失败显示真实错误,401 交给会话处理且不显示空库', async () => {
const onUnauthorized = vi.fn();
vi.mocked(getAdminAgcTemplates).mockRejectedValueOnce(
new AdminApiError({ status: 503, message: '模板服务不可用' }),
);
render(
<AdminAgcTemplatesPage token="token" onUnauthorized={onUnauthorized} />,
);
await screen.findByText('模板服务不可用');
expect(screen.queryByText('暂无模板')).toBeNull();
vi.mocked(getAdminAgcTemplates).mockRejectedValueOnce(
new AdminApiError({ status: 401, message: '会话失效' }),
);
fireEvent.click(screen.getByRole('button', { name: '刷新' }));
await waitFor(() =>
expect(onUnauthorized).toHaveBeenCalledWith('登录状态已失效'),
);
});
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
import { describe, expect, it } from 'vitest';
import {
formatAdminTimestamp,
parseAdminTimestamp,
} from './AdminErrorReportsPage';
describe('错误报告时间格式化', () => {
it('解析 SpacetimeDB seconds.microsZ 时间', () => {
expect(parseAdminTimestamp('1778207451.731746Z')).toBe(1778207451731);
});
it('解析微秒字符串并拒绝无效时间', () => {
expect(parseAdminTimestamp('1778207451731746')).toBe(1778207451731);
expect(formatAdminTimestamp('not-a-date')).toBe('-');
expect(formatAdminTimestamp('999999999999999999999999')).toBe('-');
});
});
@@ -17,6 +17,57 @@ type Props = { token: string; onUnauthorized: (message?: string) => void };
const ADMIN_ERROR_REPORT_STATUSES = ['new', 'in-progress', 'resolved'] as const;
const ERROR_REPORT_PAGE_SIZE = 50;
const ERROR_REPORT_STATUS_LABELS: Record<string, string> = {
new: '待处理',
'in-progress': '处理中',
resolved: '已解决',
};
export function parseAdminTimestamp(value: string | null | undefined) {
const normalized = value?.trim() ?? '';
if (/^-?\d+\.\d{6}Z$/u.test(normalized)) {
const [secondsText, microsText] = normalized.slice(0, -1).split('.');
const seconds = Number(secondsText);
const micros = Number(microsText);
return Number.isFinite(seconds) && Number.isFinite(micros)
? seconds * 1000 + Math.floor(micros / 1000)
: Number.NaN;
}
if (/^-?\d+$/u.test(normalized)) {
const numeric = Number(normalized);
if (!Number.isFinite(numeric)) return Number.NaN;
if (Math.abs(numeric) >= 1e14) return Math.floor(numeric / 1000);
if (Math.abs(numeric) >= 1e11) return numeric;
return numeric * 1000;
}
return Date.parse(normalized);
}
export function formatAdminTimestamp(value: string | null | undefined) {
const timestamp = parseAdminTimestamp(value);
if (!Number.isFinite(timestamp)) return '-';
const date = new Date(timestamp);
if (!Number.isFinite(date.getTime())) return '-';
try {
return new Intl.DateTimeFormat('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
}).format(date);
} catch {
return '-';
}
}
function eventText(event: Record<string, unknown>, key: string) {
const value = event[key];
return typeof value === 'string' || typeof value === 'number'
? String(value)
: '';
}
export function AdminErrorReportsPage({ token, onUnauthorized }: Props) {
const [reports, setReports] = useState<AdminErrorReportEntry[]>([]);
@@ -29,6 +80,24 @@ export function AdminErrorReportsPage({ token, onUnauthorized }: Props) {
const [pageInfo, setPageInfo] = useState({ total: 0, hasMore: false });
const openReportRequestId = useRef(0);
const loadRequestId = useRef(0);
const detailCloseButtonRef = useRef<HTMLButtonElement | null>(null);
useEffect(() => {
if (!selected) return;
const previousFocus = document.activeElement as HTMLElement | null;
const focusFrame = window.requestAnimationFrame(() => {
detailCloseButtonRef.current?.focus();
});
const handleEscape = (event: KeyboardEvent) => {
if (event.key === 'Escape') setSelected(null);
};
window.addEventListener('keydown', handleEscape);
return () => {
window.cancelAnimationFrame(focusFrame);
window.removeEventListener('keydown', handleEscape);
previousFocus?.focus?.();
};
}, [selected]);
const load = useCallback(async () => {
const requestId = ++loadRequestId.current;
@@ -184,9 +253,15 @@ export function AdminErrorReportsPage({ token, onUnauthorized }: Props) {
<td>{report.batchId}</td>
<td>{report.eventCount}</td>
<td>{report.source ?? '-'}</td>
<td>{report.status}</td>
<td>
<span
className={`admin-error-report-status is-${report.status}`}
>
{ERROR_REPORT_STATUS_LABELS[report.status] ?? report.status}
</span>
</td>
<td>{report.userId}</td>
<td>{new Date(report.createdAt).toLocaleString()}</td>
<td>{formatAdminTimestamp(report.createdAt)}</td>
</tr>
))}
</tbody>
@@ -224,66 +299,173 @@ export function AdminErrorReportsPage({ token, onUnauthorized }: Props) {
className="admin-detail-modal"
role="dialog"
aria-label="错误报告详情"
aria-modal="true"
onMouseDown={(event) => {
if (event.target === event.currentTarget) setSelected(null);
}}
>
<div className="admin-detail-modal__panel">
<header>
<h2>{selected.batchId}</h2>
<button type="button" onClick={() => setSelected(null)}>
<div className="admin-detail-modal__panel admin-error-report-detail">
<header className="admin-error-report-detail__header">
<div className="admin-error-report-detail__heading">
<span className="admin-error-report-detail__eyebrow">
</span>
<h2>{selected.batchId}</h2>
<div className="admin-error-report-detail__badges">
<span
className={`admin-error-report-status is-${selected.status}`}
>
{ERROR_REPORT_STATUS_LABELS[selected.status] ??
selected.status}
</span>
<span className="admin-error-report-detail__badge">
{selected.firstSource ?? selected.source ?? '未知来源'}
</span>
</div>
</div>
<button
type="button"
className="admin-error-report-detail__close"
ref={detailCloseButtonRef}
onClick={() => setSelected(null)}
aria-label="关闭错误报告详情"
>
×
</button>
</header>
<p>
{selected.userId} · {selected.eventCount} ·
{selected.logCount}
</p>
<pre>{JSON.stringify(selected.events.slice(0, 20), null, 2)}</pre>
{selected.userDescription ? (
<p>{selected.userDescription}</p>
) : null}
<label className="admin-detail-modal__note">
<textarea
value={selected.note ?? ''}
onChange={(event) =>
setSelected((current) =>
current
? { ...current, note: event.target.value }
: current,
)
}
maxLength={2000}
rows={4}
placeholder="记录处理结论或后续跟进事项"
disabled={busy}
/>
</label>
<div className="admin-detail-modal__actions">
<select
value={selected.status}
onChange={(event) => void saveStatus(event.target.value)}
disabled={busy}
>
{ADMIN_ERROR_REPORT_STATUSES.map((value) => (
<option key={value} value={value}>
{value}
</option>
))}
</select>
<button
type="button"
onClick={() => void saveStatus(selected.status)}
disabled={busy}
>
</button>
<button
type="button"
onClick={() => void download(selected.batchId)}
disabled={downloading}
>
{downloading ? '下载中…' : '下载诊断包'}
</button>
<div className="admin-error-report-detail__summary">
<div>
<span></span>
<strong>{selected.userId}</strong>
</div>
<div>
<span></span>
<strong>{formatAdminTimestamp(selected.createdAt)}</strong>
</div>
<div>
<span></span>
<strong>{selected.eventCount}</strong>
</div>
<div>
<span></span>
<strong>{selected.logCount}</strong>
</div>
</div>
<div className="admin-error-report-detail__body">
<section className="admin-error-report-detail__section">
<div className="admin-error-report-detail__section-heading">
<div>
<h3></h3>
<span> 20 </span>
</div>
<span className="admin-error-report-detail__count">
{selected.events.length}
</span>
</div>
<div className="admin-error-report-event-list">
{selected.events.length === 0 ? (
<p className="admin-error-report-detail__empty">
</p>
) : null}
{selected.events.slice(0, 20).map((event, index) => {
const message = eventText(event, 'message') || '未知错误';
const stack = eventText(event, 'stack');
const occurredAt = eventText(event, 'occurredAt');
return (
<article
className="admin-error-report-event"
key={`${index}-${message}`}
>
<div className="admin-error-report-event__topline">
<span className="admin-error-report-event__index">
#{index + 1}
</span>
<span>{eventText(event, 'source') || 'client'}</span>
<span>{eventText(event, 'count') || '1'} </span>
<time>{formatAdminTimestamp(occurredAt)}</time>
</div>
<p className="admin-error-report-event__message">
{message}
</p>
{eventText(event, 'fingerprint') ? (
<code>{eventText(event, 'fingerprint')}</code>
) : null}
{stack ? (
<details>
<summary></summary>
<pre>{stack}</pre>
</details>
) : null}
</article>
);
})}
</div>
</section>
{selected.userDescription ? (
<section className="admin-error-report-detail__section">
<div className="admin-error-report-detail__section-heading">
<div>
<h3></h3>
</div>
</div>
<p className="admin-error-report-detail__description">
{selected.userDescription}
</p>
</section>
) : null}
<section className="admin-error-report-detail__section admin-error-report-detail__section--note">
<label className="admin-detail-modal__note">
<span></span>
<textarea
value={selected.note ?? ''}
onChange={(event) =>
setSelected((current) =>
current
? { ...current, note: event.target.value }
: current,
)
}
maxLength={2000}
rows={4}
placeholder="记录处理结论或后续跟进事项"
disabled={busy}
/>
</label>
</section>
</div>
<footer className="admin-error-report-detail__footer">
<label className="admin-error-report-detail__status-field">
<select
value={selected.status}
onChange={(event) => void saveStatus(event.target.value)}
disabled={busy}
>
{ADMIN_ERROR_REPORT_STATUSES.map((value) => (
<option key={value} value={value}>
{ERROR_REPORT_STATUS_LABELS[value]}
</option>
))}
</select>
</label>
<div className="admin-error-report-detail__footer-actions">
<button
type="button"
onClick={() => void saveStatus(selected.status)}
disabled={busy}
>
{busy ? '保存中…' : '保存备注'}
</button>
<button
type="button"
onClick={() => void download(selected.batchId)}
disabled={downloading}
>
{downloading ? '下载中…' : '下载诊断包'}
</button>
</div>
</footer>
</div>
</div>
) : null}
@@ -154,6 +154,27 @@ test('灰度发布页可通过功能入口生成画布 Agent Gate Key', async ()
);
});
test('灰度发布页可选择模板库并默认启用零比例灰度', async () => {
const user = userEvent.setup();
render(
<AdminGrayReleaseConfigPage token="admin-token" onUnauthorized={vi.fn()} />,
);
await screen.findByRole('button', { name: 'editor.new-toolbar' });
await user.selectOptions(screen.getByLabelText('Gate Key 前缀'), ['agc']);
expect((screen.getByLabelText('Gate Key') as HTMLInputElement).value).toBe(
'agc:template-library',
);
expect(
(screen.getByLabelText('Gate Key 目标') as HTMLSelectElement).value,
).toBe('template-library');
expect((screen.getByLabelText('启用') as HTMLInputElement).checked).toBe(
true,
);
expect((screen.getByLabelText('灰度比例') as HTMLInputElement).value).toBe(
'0',
);
});
test('灰度发布页保存时转换数组和百分比', async () => {
const user = userEvent.setup();
vi.mocked(upsertAdminFeatureGateConfig).mockResolvedValueOnce({
@@ -27,9 +27,17 @@ interface GateTargetOption {
const GATE_PREFIX_LABELS: Record<string, string> = {
'image-editor': '画布',
agc: '客户端',
};
const FIXED_GATE_TARGETS: GateTargetOption[] = [
{
prefix: 'agc',
suffix: 'template-library',
key: 'agc:template-library',
label: '模板库',
description: '客户端模板库灰度',
},
{
prefix: 'image-editor',
suffix: 'agent-sidebar',
@@ -180,7 +188,7 @@ export function AdminGrayReleaseConfigPage({
setSelectedGateKey('');
setGatePrefix(option.prefix);
setGateKey(option.key);
setEnabled(false);
setEnabled(option.key === 'agc:template-library');
setRolloutPercent('0');
setAllowUserIds('');
setAllowUserTags('');
@@ -0,0 +1,288 @@
// @vitest-environment jsdom
import {
act,
cleanup,
fireEvent,
render,
screen,
waitFor,
within,
} from '@testing-library/react';
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
import {
AdminApiError,
downloadAdminProjectSnapshot,
listAdminProjectSnapshots,
} from '../api/adminApiClient';
import type { AdminProjectSnapshotEntry } from '../api/adminApiTypes';
import { AdminProjectSnapshotsPage } from './AdminProjectSnapshotsPage';
vi.mock('../api/adminApiClient', async () => ({
...(await vi.importActual<typeof import('../api/adminApiClient')>(
'../api/adminApiClient',
)),
downloadAdminProjectSnapshot: vi.fn(),
listAdminProjectSnapshots: vi.fn(),
}));
const entry: AdminProjectSnapshotEntry = {
userId: 'user-1',
projectId: 'project-1',
projectName: '三消工程',
syncRevision: 3,
syncedAtMs: 1_700_000_000_000,
fileCount: 12,
totalBytes: 2048,
status: 'ready',
};
beforeEach(() => {
vi.mocked(listAdminProjectSnapshots)
.mockReset()
.mockResolvedValue({ items: [entry], nextCursor: null });
vi.mocked(downloadAdminProjectSnapshot).mockReset();
});
afterEach(() => {
cleanup();
vi.restoreAllMocks();
vi.unstubAllGlobals();
vi.useRealTimers();
});
test('按项目展示完整性并限制未完成工程下载', async () => {
vi.mocked(listAdminProjectSnapshots).mockResolvedValue({
items: [
entry,
{
...entry,
projectId: 'partial-project',
projectName: '未完成工程',
status: 'partial',
},
{
...entry,
projectId: 'legacy-project',
projectName: null,
status: 'unverified',
},
],
nextCursor: null,
});
render(<AdminProjectSnapshotsPage token="token" onUnauthorized={vi.fn()} />);
const completeRow = (await screen.findByText('三消工程')).closest('tr')!;
expect(within(completeRow).getByText('2 KiB')).toBeTruthy();
expect(
within(completeRow)
.getByRole('button', { name: '下载完整工程' })
.hasAttribute('disabled'),
).toBe(false);
expect(
screen.getByRole('button', { name: '同步未完成' }).hasAttribute('disabled'),
).toBe(true);
expect(screen.getByText('完整性未知')).toBeTruthy();
expect(
screen
.getByRole('button', { name: '下载已存文件' })
.hasAttribute('disabled'),
).toBe(false);
});
test('加载更多合并项目,刷新失败保留列表和错误,重试从首页开始', async () => {
vi.mocked(listAdminProjectSnapshots)
.mockResolvedValueOnce({ items: [entry], nextCursor: 'page-2' })
.mockResolvedValueOnce({
items: [{ ...entry, projectId: 'project-2', projectName: '第二工程' }],
nextCursor: null,
})
.mockRejectedValueOnce(new Error('远端清单读取失败'))
.mockResolvedValueOnce({ items: [], nextCursor: null });
render(<AdminProjectSnapshotsPage token="token" onUnauthorized={vi.fn()} />);
fireEvent.click(await screen.findByRole('button', { name: '加载更多' }));
await screen.findByText('第二工程');
expect(listAdminProjectSnapshots).toHaveBeenNthCalledWith(
2,
'token',
{ cursor: 'page-2', limit: 20 },
expect.any(AbortSignal),
);
expect(screen.getByText('三消工程')).toBeTruthy();
fireEvent.click(screen.getByRole('button', { name: '刷新' }));
await screen.findByRole('alert');
expect(screen.getByText('第二工程')).toBeTruthy();
expect(screen.queryByText('暂无已上传项目')).toBeNull();
fireEvent.click(screen.getByRole('button', { name: '刷新' }));
await screen.findByText('暂无已上传项目');
expect(listAdminProjectSnapshots).toHaveBeenLastCalledWith(
'token',
{ cursor: null, limit: 20 },
expect.any(AbortSignal),
);
});
test('下载使用返回的中文文件名,随后释放对象 URL', async () => {
const createObjectURL = vi.fn(() => 'blob:archive');
const revokeObjectURL = vi.fn();
vi.stubGlobal(
'URL',
class extends URL {
static createObjectURL = createObjectURL;
static revokeObjectURL = revokeObjectURL;
},
);
let savedFilename = '';
let savedHref = '';
vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(function (
this: HTMLAnchorElement,
) {
savedFilename = this.download;
savedHref = this.href;
});
const blob = new Blob(['PK'], { type: 'application/zip' });
vi.mocked(downloadAdminProjectSnapshot).mockResolvedValue({
blob,
filename: '三消工程-r3.zip',
});
render(<AdminProjectSnapshotsPage token="token" onUnauthorized={vi.fn()} />);
const button = await screen.findByRole('button', { name: '下载完整工程' });
vi.useFakeTimers();
await act(async () => {
fireEvent.click(button);
});
expect(savedFilename).toBe('三消工程-r3.zip');
expect(savedHref).toBe('blob:archive');
expect(createObjectURL).toHaveBeenCalledWith(blob);
expect(downloadAdminProjectSnapshot).toHaveBeenCalledWith(
'token',
'user-1',
'project-1',
expect.any(AbortSignal),
);
act(() => vi.advanceTimersByTime(1000));
expect(revokeObjectURL).toHaveBeenCalledWith('blob:archive');
});
test('取消下载中止请求且不显示错误,卸载中止列表请求', async () => {
vi.mocked(downloadAdminProjectSnapshot).mockImplementation(
(_token, _user, _project, signal) =>
new Promise((_resolve, reject) => {
signal?.addEventListener('abort', () =>
reject(new DOMException('Aborted', 'AbortError')),
);
}),
);
const view = render(
<AdminProjectSnapshotsPage token="token" onUnauthorized={vi.fn()} />,
);
fireEvent.click(await screen.findByRole('button', { name: '下载完整工程' }));
fireEvent.click(await screen.findByRole('button', { name: '取消下载' }));
expect(
vi.mocked(downloadAdminProjectSnapshot).mock.calls[0]?.[3]?.aborted,
).toBe(true);
await waitFor(() => expect(screen.queryByRole('alert')).toBeNull());
vi.mocked(listAdminProjectSnapshots).mockReturnValue(new Promise(() => {}));
fireEvent.click(screen.getByRole('button', { name: '刷新' }));
const signal = vi.mocked(listAdminProjectSnapshots).mock.calls.at(-1)?.[2];
view.unmount();
expect(signal?.aborted).toBe(true);
});
test('下载登录失效走现有会话处理,403 错误保留页面', async () => {
const onUnauthorized = vi.fn();
vi.mocked(downloadAdminProjectSnapshot)
.mockRejectedValueOnce(
new AdminApiError({ status: 403, message: '无项目工程权限' }),
)
.mockRejectedValueOnce(
new AdminApiError({ status: 401, message: '已过期' }),
);
render(
<AdminProjectSnapshotsPage token="token" onUnauthorized={onUnauthorized} />,
);
fireEvent.click(await screen.findByRole('button', { name: '下载完整工程' }));
expect(await screen.findByText('无项目工程权限')).toBeTruthy();
expect(onUnauthorized).not.toHaveBeenCalled();
fireEvent.click(screen.getByRole('button', { name: '下载完整工程' }));
await waitFor(() =>
expect(onUnauthorized).toHaveBeenCalledWith('登录状态已失效'),
);
});
test('首次列表失败显示错误而非空项目,401 失效回到会话处理', async () => {
const onUnauthorized = vi.fn();
vi.mocked(listAdminProjectSnapshots)
.mockRejectedValueOnce(new Error('清单存储不可用'))
.mockRejectedValueOnce(
new AdminApiError({ status: 401, message: '已过期' }),
);
render(
<AdminProjectSnapshotsPage token="token" onUnauthorized={onUnauthorized} />,
);
await screen.findByText('清单存储不可用');
expect(screen.queryByText('暂无已上传项目')).toBeNull();
fireEvent.click(screen.getByRole('button', { name: '刷新' }));
await waitFor(() =>
expect(onUnauthorized).toHaveBeenCalledWith('登录状态已失效'),
);
});
test('更换登录令牌丢弃旧列表和晚返回请求', async () => {
let finishOldRequest!: (value: {
items: AdminProjectSnapshotEntry[];
nextCursor: null;
}) => void;
vi.mocked(listAdminProjectSnapshots)
.mockReturnValueOnce(
new Promise((resolve) => {
finishOldRequest = resolve;
}),
)
.mockResolvedValueOnce({
items: [{ ...entry, projectName: '新账号工程' }],
nextCursor: null,
});
const onUnauthorized = vi.fn();
const view = render(
<AdminProjectSnapshotsPage
token="old-token"
onUnauthorized={onUnauthorized}
/>,
);
const oldSignal = vi.mocked(listAdminProjectSnapshots).mock.calls[0]?.[2];
view.rerender(
<AdminProjectSnapshotsPage
token="new-token"
onUnauthorized={onUnauthorized}
/>,
);
await screen.findByText('新账号工程');
expect(oldSignal?.aborted).toBe(true);
await act(async () => {
finishOldRequest({ items: [entry], nextCursor: null });
});
expect(screen.queryByText('三消工程')).toBeNull();
expect(screen.getByText('新账号工程')).toBeTruthy();
});
test('卸载后完成的下载不会创建浏览器文件', async () => {
let finishDownload!: (value: { blob: Blob; filename: string }) => void;
vi.mocked(downloadAdminProjectSnapshot).mockReturnValue(
new Promise((resolve) => {
finishDownload = resolve;
}),
);
const click = vi
.spyOn(HTMLAnchorElement.prototype, 'click')
.mockImplementation(() => {});
const view = render(
<AdminProjectSnapshotsPage token="token" onUnauthorized={vi.fn()} />,
);
fireEvent.click(await screen.findByRole('button', { name: '下载完整工程' }));
const signal = vi.mocked(downloadAdminProjectSnapshot).mock.calls[0]?.[3];
view.unmount();
expect(signal?.aborted).toBe(true);
await act(async () => {
finishDownload({ blob: new Blob(['PK']), filename: 'old.zip' });
});
expect(click).not.toHaveBeenCalled();
});
@@ -0,0 +1,270 @@
import { Download, RefreshCcw, X } from 'lucide-react';
import { useCallback, useEffect, useRef, useState } from 'react';
import {
downloadAdminProjectSnapshot,
listAdminProjectSnapshots,
} from '../api/adminApiClient';
import type { AdminProjectSnapshotEntry } from '../api/adminApiTypes';
import { handlePageError } from './pageUtils';
interface AdminProjectSnapshotsPageProps {
token: string;
onUnauthorized: (message?: string) => void;
}
const snapshotStatuses = {
ready: {
label: '已同步',
className: 'admin-status-ok',
action: '下载完整工程',
},
partial: {
label: '同步未完成',
className: 'admin-status-pending',
action: '同步未完成',
},
unverified: {
label: '完整性未知',
className: 'admin-status-pending',
action: '下载已存文件',
},
};
export function AdminProjectSnapshotsPage({
token,
onUnauthorized,
}: AdminProjectSnapshotsPageProps) {
const [items, setItems] = useState<AdminProjectSnapshotEntry[]>([]);
const [nextCursor, setNextCursor] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [hasLoaded, setHasLoaded] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
const [downloadingKey, setDownloadingKey] = useState<string | null>(null);
const listController = useRef<AbortController | null>(null);
const downloadController = useRef<AbortController | null>(null);
const loadPage = useCallback(
async (cursor: string | null = null) => {
listController.current?.abort();
const controller = new AbortController();
listController.current = controller;
setIsLoading(true);
setErrorMessage('');
try {
const response = await listAdminProjectSnapshots(
token,
{ cursor, limit: 20 },
controller.signal,
);
if (controller.signal.aborted) return;
setItems((current) => {
if (!cursor) return response.items;
const entries = new Map(
current.map((entry) => [snapshotKey(entry), entry]),
);
response.items.forEach((entry) =>
entries.set(snapshotKey(entry), entry),
);
return [...entries.values()];
});
setNextCursor(response.nextCursor);
setHasLoaded(true);
} catch (error: unknown) {
if (!controller.signal.aborted)
handlePageError(error, onUnauthorized, setErrorMessage);
} finally {
if (listController.current === controller) {
listController.current = null;
setIsLoading(false);
}
}
},
[token, onUnauthorized],
);
useEffect(() => {
setItems([]);
setNextCursor(null);
setHasLoaded(false);
setDownloadingKey(null);
void loadPage();
return () => {
listController.current?.abort();
listController.current = null;
downloadController.current?.abort();
downloadController.current = null;
};
}, [loadPage]);
async function downloadProject(entry: AdminProjectSnapshotEntry) {
if (downloadController.current || entry.status === 'partial') return;
const controller = new AbortController();
downloadController.current = controller;
setDownloadingKey(snapshotKey(entry));
setErrorMessage('');
try {
const archive = await downloadAdminProjectSnapshot(
token,
entry.userId,
entry.projectId,
controller.signal,
);
if (controller.signal.aborted) return;
const objectUrl = URL.createObjectURL(archive.blob);
const link = document.createElement('a');
link.href = objectUrl;
link.download = archive.filename;
document.body.append(link);
try {
link.click();
} finally {
link.remove();
// 给浏览器时间接管下载,随后释放临时 URL。
setTimeout(() => URL.revokeObjectURL(objectUrl), 1000);
}
} catch (error: unknown) {
if (!controller.signal.aborted)
handlePageError(error, onUnauthorized, setErrorMessage);
} finally {
if (downloadController.current === controller) {
downloadController.current = null;
setDownloadingKey(null);
}
}
}
function cancelDownload() {
downloadController.current?.abort();
downloadController.current = null;
setDownloadingKey(null);
}
return (
<section className="admin-page admin-page-wide">
<div className="admin-page-heading">
<h2></h2>
<button
className="admin-secondary-button"
disabled={isLoading}
type="button"
onClick={() => void loadPage()}
>
<RefreshCcw size={17} aria-hidden="true" />
<span>{isLoading ? '加载中' : '刷新'}</span>
</button>
</div>
{errorMessage ? (
<div className="admin-alert" role="alert">
{errorMessage}
</div>
) : null}
<section
className="admin-panel admin-stack"
aria-label="项目工程列表"
aria-busy={isLoading}
>
<div className="admin-table-wrap">
<table className="admin-table admin-project-snapshot-table">
<thead>
<tr>
<th></th>
<th> ID</th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{items.map((entry) => {
const status = snapshotStatuses[entry.status];
const isDownloading = downloadingKey === snapshotKey(entry);
return (
<tr key={snapshotKey(entry)}>
<td data-label="项目">
<strong>{entry.projectName || entry.projectId}</strong>
<small>{entry.projectId}</small>
</td>
<td data-label="用户 ID">{entry.userId}</td>
<td data-label="同步时间">
<span>
{new Date(entry.syncedAtMs).toLocaleString('zh-CN', {
hour12: false,
})}
<small> {entry.syncRevision}</small>
</span>
</td>
<td data-label="文件数">
{entry.fileCount.toLocaleString('zh-CN')}
</td>
<td data-label="体积">{formatBytes(entry.totalBytes)}</td>
<td data-label="完整性">
<span className={`admin-status ${status.className}`}>
{status.label}
</span>
</td>
<td data-label="操作">
{isDownloading ? (
<button
className="admin-secondary-button"
type="button"
onClick={cancelDownload}
>
<X size={16} aria-hidden="true" />
<span></span>
</button>
) : (
<button
className="admin-secondary-button"
disabled={
entry.status === 'partial' ||
downloadingKey !== null
}
type="button"
onClick={() => void downloadProject(entry)}
>
<Download size={16} aria-hidden="true" />
<span>{status.action}</span>
</button>
)}
</td>
</tr>
);
})}
</tbody>
</table>
</div>
{hasLoaded && items.length === 0 && !errorMessage ? (
<p className="admin-muted-text"></p>
) : null}
{nextCursor ? (
<div className="admin-action-row">
<button
className="admin-secondary-button"
disabled={isLoading}
type="button"
onClick={() => void loadPage(nextCursor)}
>
{isLoading ? '加载中' : '加载更多'}
</button>
</div>
) : null}
</section>
</section>
);
}
function snapshotKey(entry: AdminProjectSnapshotEntry) {
return `${entry.userId}/${entry.projectId}`;
}
function formatBytes(bytes: number) {
const units = ['B', 'KiB', 'MiB', 'GiB'];
const unit = Math.min(
Math.floor(Math.log2(Math.max(1, bytes)) / 10),
units.length - 1,
);
return `${(bytes / 1024 ** unit).toLocaleString('zh-CN', { maximumFractionDigits: 1 })} ${units[unit]}`;
}
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More