Compare commits

..

40 Commits

Author SHA1 Message Date
menghao 6dca066a87 Merge branch 'master' into codex/game-agent-run
Project CI / Repository checks (pull_request) Successful in 1m6s
Project CI / Frontend tests (pull_request) Successful in 3m15s
Project CI / Backend tests (pull_request) Successful in 4m11s
Project CI / Native shell tests (pull_request) Successful in 16m43s
2026-08-07 19:53:30 +08:00
menghao 02ae3d3cad 合并最新主线并解决共享记忆冲突
Project CI / Backend tests (pull_request) Failing after 8s
Project CI / Repository checks (pull_request) Failing after 7s
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Successful in 3m0s
同步 origin/master 的 CI 与生成结果原子提交改动

保留双方 pitfalls 排障记录
2026-08-07 19:50:14 +08:00
menghao d04339ad7f 修复自主完成合同测试竞态
Project CI / Repository checks (pull_request) Successful in 1m23s
Project CI / Native shell tests (pull_request) Successful in 12m15s
Project CI / Frontend tests (pull_request) Successful in 3m45s
Project CI / Backend tests (pull_request) Successful in 4m12s
隔离 readiness 状态投影,避免测试重复调度 preview-playtest child
补充 Runtime 终态投影测试的竞态避坑说明
2026-08-07 13:46:16 +08:00
menghao 6fe52a92bb 修复合并后的仓库检查失败
Project CI / Repository checks (pull_request) Successful in 1m8s
Project CI / Frontend tests (pull_request) Successful in 3m2s
Project CI / Backend tests (pull_request) Successful in 3m42s
Project CI / Native shell tests (pull_request) Failing after 9m28s
删除重复维护页原子替换函数并恢复符号链接防护
清理开发栈未使用变量与资源投影正则告警
统一相关测试导入与开发栈导出顺序
2026-08-07 12:44:29 +08:00
menghao 75e3f1d041 合并远端主线并解决游戏创作冲突
Project CI / Repository checks (pull_request) Failing after 33s
Project CI / Backend tests (pull_request) Successful in 3m45s
Project CI / Native shell tests (pull_request) Successful in 13m59s
Project CI / Frontend tests (pull_request) Successful in 3m1s
融合主线 Runtime、安全修复与提示词资源
保留可运行版本、资源替换和数值微调能力
修复资源画布布局、焦点与依赖关系融合
补齐 manifest 并发保护与 Tetris 场景兼容
同步前端测试、Rust 回归测试与权威文档
2026-08-07 12:15:12 +08:00
menghao 8a4b71bf8e 修复资源画布溢出与模板资源投影
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Repository checks (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Successful in 3m3s
Project CI / Native shell tests (pull_request) Successful in 10m23s
闭合资源画布横向收缩链并增加内部滚动条

过滤任务产物通配模板避免生成无效资源卡

补充布局与资源投影回归测试

同步工作台文档与共享排障记忆
2026-08-07 10:53:41 +08:00
menghao 53e37ea361 支持App Run并行开发并修复资源画布
Project CI / Repository checks (pull_request) Failing after 10s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Successful in 3m28s
Project CI / Native shell tests (pull_request) Failing after 9m27s
新增App Run独立开发profile并隔离端口、数据库、Tauri身份与AppData
常驻资源排列切换并避免布局状态挤压操作入口
显式扩展资源画布横向滚动范围并收敛依赖图可见连线
补充启动器、资源布局、界面回归测试与项目文档
2026-08-07 10:13:25 +08:00
menghao dc7c7dc6cc 完成P7验收修复与CI稳定性加固
Project CI / Repository checks (pull_request) Failing after 10s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / Frontend tests (pull_request) Failing after 18s
Project CI / Native shell tests (pull_request) Failing after 7m24s
修正工作台前端 lint 与正式测试切片初始化依赖
补齐可调参数完成态测试夹具并稳定生成恢复 HTTP 读取
修正 macOS Mach-O release 产物识别与原生壳门禁
同步阶段七验收文档与长期排障记忆
2026-08-05 10:00:09 +08:00
menghao 3ec3a09380 完成游戏工作台资源替换与数值微调
实现可信资源规格校验与不可变资源替换子版本
新增登记参数读取、暂停态写入和会话失效链路
补齐前后端合同、生成产物约束与回归测试
同步工作台 PRD、技术方案和共享决策记录
2026-08-04 18:38:41 +08:00
menghao 752505547d 实现运行工作台正式测试切片与悬停信息
Project CI / Frontend tests (pull_request) Failing after 23s
Project CI / Repository checks (pull_request) Failing after 1m4s
Project CI / Backend tests (pull_request) Successful in 4m59s
Project CI / Native shell tests (pull_request) Successful in 13m17s
新增正式测试切片合同、状态机与上一项/播放暂停/下一项控制
新增可信悬停资源解析与资源管理画布定位
扩展 Tauri 版本登记、运行 Bridge 和 Agent 生成合同
补齐 TypeScript、Rust 与 AppSurface 回归测试
同步工作台 PRD、技术方案与共享决策记录
保持 P2 资源替换、参数写入和下一版本能力未接线
2026-08-04 17:06:24 +08:00
menghao 203ce5b9e7 实现游戏运行会话与桥接协议
Project CI / Backend tests (pull_request) Successful in 3m49s
Project CI / Native shell tests (pull_request) Failing after 8m26s
Project CI / Frontend tests (pull_request) Failing after 25s
Project CI / Repository checks (pull_request) Failing after 54s
冻结 Host/Game Bridge 双向消息合同与安全校验
为 PreviewRegistry 增加预览身份并注入宿主 bootstrap
接入 iframe 一次性会话、状态投影和暂停继续控制
补充协议、AppSurface、Rust 生命周期与共享合同测试
同步更新工作台 PRD、技术方案和共享决策记录
2026-08-04 16:16:04 +08:00
menghao d1a47c0fe7 实现游戏工作台P0/P1可运行版本能力
Project CI / Frontend tests (pull_request) Successful in 3m43s
Project CI / Repository checks (pull_request) Failing after 1m27s
Project CI / Backend tests (pull_request) Successful in 4m26s
Project CI / Native shell tests (pull_request) Failing after 7m40s
冻结工作台运行版本、会话、参数、资源与消息跨端合同
新增可信 Runtime 验证后的不可变可运行版本登记与快照校验
支持 Tauri 客户端选择、切换并运行历史版本快照
收紧普通预览授权边界并同步资源高亮与明确错误展示
补充前后端测试、PRD、技术方案和项目决策记录
2026-08-04 15:21:55 +08:00
menghao 2e0ef02fda 修复CI依赖准备顺序
Project CI / Repository checks (pull_request) Successful in 1m1s
Project CI / Frontend tests (pull_request) Successful in 3m7s
Project CI / Backend tests (pull_request) Successful in 3m48s
Project CI / Native shell tests (pull_request) Successful in 10m55s
修复依赖图测试import排序门禁

在后端构建门禁前预取锁定Cargo依赖

新增Backend-CI执行顺序回归测试

同步更新Gitea-CI开发运维约定
2026-08-04 12:00:04 +08:00
menghao ab16c87c5d 调整资源依赖视图连线规则
Project CI / Repository checks (pull_request) Failing after 48s
Project CI / Backend tests (pull_request) Failing after 29s
Project CI / Frontend tests (pull_request) Successful in 2m54s
Project CI / Native shell tests (pull_request) Successful in 10m33s
限制任务虚线仅在同资源类型分区内渲染
调亮精确资源引用橙线并保持持续展示
取消资源点击后的依赖高亮与无关连线弱化
补充依赖图单元测试与工作台集成测试
同步更新项目开发工作台 PRD
2026-08-04 11:20:58 +08:00
menghao 6d8c7ae496 修复前端异步读取卸载竞态
Project CI / Repository checks (pull_request) Successful in 55s
Project CI / Backend tests (pull_request) Successful in 4m13s
Project CI / Native shell tests (pull_request) Successful in 11m16s
Project CI / Frontend tests (pull_request) Successful in 2m29s
未登录画布不再触发受保护的钱包刷新
充值中心读取在换代与卸载时中止并失效
补充卸载取消回归测试与共享排障经验
2026-08-04 10:38:28 +08:00
menghao 80bcb4ba0e 完成资源管理阶段七验收
Project CI / Repository checks (pull_request) Successful in 1m1s
Project CI / Backend tests (pull_request) Successful in 3m43s
Project CI / Native shell tests (pull_request) Successful in 10m47s
Project CI / Frontend tests (pull_request) Failing after 1m41s
补齐视频与资源读取失败空态测试
完成全量前后端和 Rust CI 验收
修复 macOS/BSD 跨平台门禁与测试隔离
更新阶段七验收与共享项目记忆
2026-08-04 10:06:12 +08:00
menghao df4e61a208 合并最新主分支
同步 Runtime、外部 API 与统一弹窗等主分支更新

解决开发栈进程探活、Runtime continuation 与启动测试冲突

保留资源依赖图排障记忆并验证冲突路径
2026-08-03 19:21:12 +08:00
menghao fef53b634e 完成正式项目版本阶段六
Project CI / Repository checks (pull_request) Failing after 43s
Project CI / Frontend tests (pull_request) Failing after 2m6s
Project CI / Backend tests (pull_request) Successful in 3m37s
Project CI / Native shell tests (pull_request) Successful in 11m49s
扩展 manifest 正式迭代版本合同并强制历史记录追加不可变
接入版本卡、父子关系与绑定资源高亮
补齐共享契约、Tauri 存储与工作台回归测试
同步工作台 PRD、技术方案与项目记忆
2026-08-03 19:04:01 +08:00
menghao ba996aad80 完成资源依赖排列阶段五
Project CI / Frontend tests (pull_request) Failing after 21s
Project CI / Repository checks (pull_request) Successful in 1m1s
Project CI / Backend tests (pull_request) Successful in 3m54s
Project CI / Native shell tests (pull_request) Successful in 11m49s
计算资源引用 SCC 与任务深度下限
拆分 producer assignment 和布局深度合同
补齐依赖布局回归测试
同步工作台 PRD、技术方案与项目记忆
2026-08-03 17:51:59 +08:00
menghao d374f3292a 完成资源聚焦阶段四
Project CI / Repository checks (pull_request) Successful in 1m16s
Project CI / Frontend tests (pull_request) Failing after 2m37s
Project CI / Backend tests (pull_request) Successful in 3m29s
Project CI / Native shell tests (pull_request) Successful in 11m21s
新增项目文档与媒体资源安全读取命令
补齐Markdown、扩展美术媒体与音频聚焦展示
增加安全边界与工作台回归测试
同步PRD、技术方案与共享决策
2026-08-03 17:20:14 +08:00
menghao 2c65878b60 完成资源分类投影与中央聚焦
Project CI / Repository checks (pull_request) Failing after 49s
Project CI / Frontend tests (pull_request) Failing after 2m16s
Project CI / Backend tests (pull_request) Successful in 3m55s
Project CI / Native shell tests (pull_request) Successful in 11m17s
收口文档、版本、美术和音频资源分类
将资源详情改为中央主视窗聚焦并恢复列表上下文
补充资源身份、聚焦状态和依赖图回归测试
同步工作台PRD、技术方案和项目决策记录
2026-08-03 16:39:43 +08:00
menghao 7339bb5da1 暂缓资源卡拖动并统一资源管理合同
Project CI / Repository checks (pull_request) Failing after 10s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Failing after 2m18s
Project CI / Native shell tests (pull_request) Successful in 11m37s
移除资源卡 Pointer 拖动入口并保留点击详情
改写交互与布局测试,覆盖指针不改坐标且不提交 CAS
同步工作台 PRD、技术方案和项目决策记录
2026-08-03 15:35:54 +08:00
menghao c24e3010f8 Merge remote-tracking branch 'origin/master' into codex/dependency-graph
Project CI / Repository checks (pull_request) Successful in 1m3s
Project CI / Frontend tests (pull_request) Failing after 2m7s
Project CI / Backend tests (pull_request) Successful in 3m25s
Project CI / Native shell tests (pull_request) Successful in 10m41s
2026-08-03 14:24:46 +08:00
menghao d8064eff49 同步自主首批任务图测试夹具
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / Frontend tests (pull_request) Successful in 2m46s
Project CI / Native shell tests (pull_request) Successful in 10m54s
补齐可信 Supervisor 根任务与三 Leader 首批合同测试数据

更新视觉失败和 Ready 任务调度断言以匹配最新任务图

修复 Provider 重试恢复响应仍使用旧底层 Agent 合同的问题
2026-08-03 14:21:30 +08:00
menghao bda0d0d398 修复Linux进程树探活与Runtime默认栈溢出
Project CI / Repository checks (pull_request) Successful in 1m7s
Project CI / Frontend tests (pull_request) Successful in 3m0s
Project CI / Backend tests (pull_request) Successful in 3m32s
Project CI / Native shell tests (pull_request) Failing after 7m38s
Linux进程组探活忽略已终止但未回收的zombie成员

pending恢复与后续队列统一跨越装箱的Tokio任务边界

补充Tauri生命周期和Runtime默认栈回归测试与文档
2026-08-03 13:34:25 +08:00
menghao b95da30721 Merge branch 'master' into codex/dependency-graph
Project CI / Repository checks (pull_request) Successful in 54s
Project CI / Frontend tests (pull_request) Failing after 2m9s
Project CI / Backend tests (pull_request) Successful in 3m46s
Project CI / Native shell tests (pull_request) Failing after 3m4s
2026-08-03 11:55:21 +08:00
menghao 7f038490f1 修复运行时恢复路径栈溢出
Project CI / Repository checks (pull_request) Failing after 7s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Successful in 2m51s
Project CI / Native shell tests (pull_request) Failing after 7m51s
为 pending continuation 与后台主循环增加可取消的 Tokio 任务边界
保留 durable batch 恢复防重和父任务取消语义
补充运行时技术约束与共享排障记录
2026-08-03 11:53:59 +08:00
menghao 05c608214e 修复资源依赖图布局初始化与拖动性能
Project CI / Repository checks (pull_request) Successful in 1m0s
Project CI / Frontend tests (pull_request) Successful in 3m4s
Project CI / Backend tests (pull_request) Successful in 3m29s
Project CI / Native shell tests (pull_request) Failing after 5m34s
将任务依赖深度迁移到 Rust SCC 压缩图并返回稳定结果

为 dependency 模式增加图加载屏障并重派生自动布局

通过 DOM 热路径和局部 SVG 缓存避免拖动全量 React 重渲染

补充 4096 资源、异步初始化及布局保持回归测试

同步工作台 PRD、技术方案与共享项目记忆
2026-08-03 11:16:02 +08:00
menghao 37f4a63112 合并最新master并解决工作台冲突
Project CI / Repository checks (pull_request) Successful in 1m17s
Project CI / Frontend tests (pull_request) Successful in 3m13s
Project CI / Backend tests (pull_request) Successful in 3m45s
Project CI / Native shell tests (pull_request) Failing after 5m39s
合并 master 最新游戏聊天前端 Lint 修复。

保留依赖图分支的 Hook 稳定化逻辑并统一注释格式。
2026-08-03 10:35:49 +08:00
menghao fe34eee052 合并最新master并解决工作台冲突
合并game-chat五分钟首版与平台美术硬门更新
解决App响应流Hook的等价内容冲突
稳定新增消息追加回调并整理合并后的导入顺序
通过前端完整测试、Lint、类型检查和Rust tests编译
2026-08-03 10:30:48 +08:00
menghao cced839da5 修复合并后的前端Lint门禁
Project CI / Repository checks (pull_request) Successful in 55s
Project CI / Frontend tests (pull_request) Successful in 2m55s
Project CI / Native shell tests (pull_request) Successful in 11m45s
Project CI / Backend tests (pull_request) Successful in 3m54s
稳定项目总控响应流更新回调并补齐Effect依赖
调整Agent Runtime模型测试的导入顺序
通过完整ESLint、类型检查和AppSurface回归测试
2026-07-31 19:31:24 +08:00
menghao e77d187497 合并最新master并解决资源图冲突
Project CI / Repository checks (pull_request) Failing after 47s
Project CI / Frontend tests (pull_request) Successful in 3m21s
Project CI / Backend tests (pull_request) Successful in 3m49s
Project CI / Native shell tests (pull_request) Successful in 11m42s
合并远端master的运行收口、聊天输出与画布交互更新
保留资源依赖图只读命令注册和Rust测试隔离配置
解决main.rs入口附近的内容冲突
完成依赖图、退出保护及完整前端回归验证
2026-07-31 19:19:05 +08:00
menghao 29dce59b35 将资源依赖图计算迁移到Rust
Project CI / Repository checks (pull_request) Failing after 10s
Project CI / Backend tests (pull_request) Failing after 7s
Project CI / Frontend tests (pull_request) Successful in 2m50s
Project CI / Native shell tests (pull_request) Successful in 11m33s
新增只读资源依赖图模型,完成过滤、去重、环检测和任务流聚合
前端SVG仅负责几何渲染、搜索联动、选中高亮和拖动预览
补充资源图、覆盖层和工作台回归测试
同步更新工作台PRD、技术方案与项目记忆
2026-07-31 19:09:57 +08:00
menghao f582ecf032 优化资源依赖图连线外观
Project CI / Repository checks (pull_request) Failing after 11s
Project CI / Backend tests (pull_request) Failing after 10s
Project CI / Frontend tests (pull_request) Successful in 3m5s
Project CI / Native shell tests (pull_request) Successful in 10m26s
将任务流分支改为平滑贝塞尔曲线
调整两类连线的线宽、虚线和箭头样式
补充连线几何测试与相关文档
2026-07-31 18:36:21 +08:00
menghao a431ab47cd 合并最新 master 并解决依赖图文档冲突
Project CI / Frontend tests (pull_request) Successful in 3m7s
Project CI / Backend tests (pull_request) Successful in 3m37s
Project CI / Native shell tests (pull_request) Successful in 12m20s
Project CI / Repository checks (pull_request) Successful in 59s
保留资源依赖图的纯前端 SVG 决策记录
保留主分支新增的抠图元数据与 OpenAPI 决策记录
同步 master 最新代码并保持依赖图与后端测试修复
2026-07-31 16:50:59 +08:00
menghao 11140b9fb6 修复后端源码约束测试
Project CI / Native shell tests (pull_request) Successful in 11m56s
Project CI / Repository checks (pull_request) Successful in 59s
Project CI / Frontend tests (pull_request) Successful in 3m35s
Project CI / Backend tests (pull_request) Successful in 3m49s
同步 UI 素材切片持久化失败断言至现有 warning 映射函数
保持生产逻辑和持久层不变
2026-07-31 16:31:09 +08:00
menghao 246fd1d9d6 同步最新 master 修复 CI 基线
Project CI / Repository checks (pull_request) Successful in 1m15s
Project CI / Frontend tests (pull_request) Successful in 3m23s
Project CI / Native shell tests (pull_request) Successful in 11m51s
Project CI / Backend tests (pull_request) Failing after 2m47s
合并画板素材上传后的私有图片预览修复。

确保 PR 分支包含当前 master 基础提交。
2026-07-31 16:05:05 +08:00
menghao 68ea9bdff3 合并最新 master 并解决资源依赖图冲突
Project CI / Repository checks (pull_request) Failing after 7s
Project CI / Frontend tests (pull_request) Failing after 2m19s
Project CI / Backend tests (pull_request) Failing after 8s
Project CI / Native shell tests (pull_request) Successful in 12m34s
同步 mentor 已合入 master 的最新工程改动。

保留资源依赖图决策记录并整合 master 的新增决策。

# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
2026-07-31 15:54:40 +08:00
menghao 0b6be155dd 合并最新 AI 游戏创作 App 分支
Project CI / Frontend tests (pull_request) Successful in 3m18s
Project CI / Repository checks (pull_request) Failing after 22s
Project CI / Backend tests (pull_request) Failing after 22s
Project CI / Native shell tests (pull_request) Failing after 22s
2026-07-31 15:34:36 +08:00
menghao 8531a9af3e 实现资源依赖关系图层
Project CI / Frontend tests (pull_request) Successful in 2m40s
Project CI / Native shell tests (pull_request) Successful in 10m40s
Project CI / Repository checks (pull_request) Failing after 7s
Project CI / Backend tests (pull_request) Failing after 7s
新增资源依赖图模型、去重、环检测与上下游查询。

使用原生 SVG 渲染资源引用和聚合任务流,并接入搜索、高亮、拖动及生命周期清理。

修复资源自引用连线被卡片遮挡的问题。

补充前端回归测试并同步工作台 PRD、技术方案和决策记录。
2026-07-31 15:13:18 +08:00
1651 changed files with 95089 additions and 386469 deletions
-16
View File
@@ -1,16 +0,0 @@
# Genarrative Codex 项目工具
`.codex/` 是仓库级 Codex 工具目录,保存项目共享的 skills、插件资源、hooks 和相关配置模板。它只描述如何协作和加载工具,不承载项目业务知识。
## 目录约定
- `.codex/skills/` 是项目专属 skill 根目录。每个 skill 以目录中的 `SKILL.md` 为入口,配套的参考资料和脚本放在同一目录下。
- `.codex/plugins/` 保存随仓库分发的项目插件资源及其参考资料。当前的 `game-studio` 插件提供浏览器游戏设计、原型、2D/3D 技术栈、素材管线和 playtest 工作流;是否启用遵循当前 Codex 的插件加载机制,不依赖旧工具的环境变量或个人配置脚本。
- `.codex/hooks/``.codex/environments/` 等目录保存项目工具链所需的 hooks 和环境模板;它们不替代项目代码中的运行时配置。
- 长期有效的产品、架构、接口、排障和协作知识统一放在 `docs/``docs/project-memory/`,不复制到本目录。
## 使用边界
进入仓库后先读根目录 `AGENTS.md`,再按任务路由读取对应 skill。SpacetimeDB 的通用概念、Rust 服务端、CLI、TypeScript 客户端和 MCP 用法由已安装的官方插件提供;项目约束和入口由 `.codex/skills/genarrative-spacetimedb/SKILL.md` 统一编排。
个人 `~/.codex` 配置、凭据、会话、环境变量和本地路径不得复制到仓库。若本目录内容与当前代码或最新 `docs/` 冲突,以代码和最新文档为准,并修正过期工具说明。
-7
View File
@@ -1,7 +0,0 @@
# Genarrative 项目 skills
`.codex/skills/` 是仓库级、可通过 Git 共享的项目专属 skill 根目录。每个目录的 `SKILL.md` 是唯一入口;较长的参考资料、示例和脚本放在该 skill 的 `references/``assets/``scripts/` 下。
项目 skill 负责把当前仓库的目录、契约、验证门禁和历史约束串起来,不重复维护通用框架知识。涉及 SpacetimeDB 时,先读 `genarrative-spacetimedb/SKILL.md`,由它路由到已安装的官方 SpacetimeDB 插件 skills。
长期项目知识放在 `docs/``docs/project-memory/`;不要把个人配置、密钥、会话、缓存或临时计划写入本目录。发现 skill 与代码或最新文档不一致时,按当前实现更新 skill,并同步必要的项目文档。
@@ -17,7 +17,7 @@ Prefer `scripts/genarrative_external_api.py` for runnable REST calls. It uses on
2. Before the first generation in a new conversation, obtain a canvas name unless an existing `projectId` and `assetFolderId` were supplied. Create or reuse a project and a same-name asset-library folder. Retain `canvasName`, `projectId`, `assetFolderId`, and the current art spec.
3. Normalize art requests into a reusable spec. Ask only for missing values that block the selected operation. Reuse the spec until the user changes its style, subject family, palette, format, or constraints.
4. Infer the operation from the user's intent. Do not ask the user to select an API unless two operations would produce materially different artifacts.
5. If a reference exists only as a local file, upload and confirm it first. Pass the stable returned `objectKey` to operations that accept object references; never substitute a temporary signed URL. For an icon-spritesheet primary spec, additionally create a project resource or asset record with `assetKind="icon-spec"`, then pass the returned resource or asset ID as `referenceId`.
5. If a reference exists only as a local file, upload and confirm it first. Pass the stable returned `objectKey` to generation; never substitute a temporary signed URL.
6. For generation endpoints that support the fields, include `projectId`, `assetFolderId`, an asset label, and `canvasCompletion` so the result enters both the canvas and its same-name library folder.
7. Treat every generation POST as asynchronous. Send one stable `Idempotency-Key` per logical request, retain the returned `operationId`, and poll the returned `statusUrl` or `GET /api/external/v1/generations/{operationId}` according to `pollAfterMs`.
8. Consume `result` only after `status=completed`. On `failed`, surface the safe error. On a client timeout or lost response, retain the operation/key; do not create a replacement request.
@@ -27,12 +27,11 @@ Prefer `scripts/genarrative_external_api.py` for runnable REST calls. It uses on
## Essential Invariants
- Authenticate MCP and business API calls with `Authorization: Bearer <tnr_sk_...>`. Never ask the user to paste a key into chat or place one in repository files.
- All nine generation POST routes require `Idempotency-Key` and return HTTP `202`; `202` is durable acceptance, not a media result.
- All eight generation POST routes require `Idempotency-Key` and return HTTP `202`; `202` is durable acceptance, not a media result.
- Retry an uncertain submission only with the exact same body and the same idempotency key. A polling timeout is not permission to generate again.
- Use stable references such as `objectKey`, project resource ID, or asset ID where each operation permits them. Image edit/redraw is stricter: `sourceReferenceId` accepts only a registered project resource ID or asset ID; upload confirmation alone is not enough. Use `/assets/read-url` only for temporary preview/download access.
- Use stable references such as `objectKey`, project resource ID, or asset ID in generation requests. Use `/assets/read-url` only for temporary preview/download access.
- Preserve both warning channels after completion. A general `warning` can coexist with `sliceWarning`; do not discard either.
- Do not invent missing derivatives. A source-preserved warning means the main source remains usable but requested post-processing failed. A slice warning means the complete transparent sheet is usable but individual slices are absent.
- For successful `style="pixelArt"`, treat completed-result and nested resource/asset dimensions as the final logical-grid PNG dimensions. They may differ from `size`, `imageSize`, the provider image, and `canvasCompletion.placeholder`; do not rescale or reject the artifact to match those inputs.
- Keep generated artifacts in the canvas and asset library together. Character animation accepts `assetFolderId` and `assetLabel`; its completed result directly returns the final `assetKind="character-animation"` resource and asset with formal sequence fields. Do not create a duplicate first-frame record.
## Documentation Navigation
@@ -106,21 +105,6 @@ client.generate_image(
)
```
For background removal, pass a stable owner-scoped object key, project resource ID, or asset ID; the helper keeps the same asynchronous submission and polling contract:
```python
session = client.prepare_canvas_session("去背景画布")
client.remove_background(
"editor-upload/object.png",
source_width=720,
source_height=1280,
canvasSession=session,
assetLabel="去背景结果",
)
```
Background removal preserves the source pixel size. For normal canvas placement with `canvasSession`, pass the real `source_width` and `source_height`, or provide both `canvasWidth` and `canvasHeight`; the helper rejects missing dimensions instead of guessing a square placeholder. `assetKind` may only describe a static image and must match the authoritative source record. Prefer a project resource ID or asset ID when the same object key has multiple semantic registrations; for a raw object key outside in-place replacement, pass `sourceResourceId` to disambiguate. Passing `targetLayerId` selects in-place replacement: the helper retains the session's project/library context but does not inject `canvasCompletion`, and it rejects an explicit `canvasCompletion` combined with `targetLayerId`. The target layer must point to the same authoritative object as the source, and the server durably binds a raw object key to that target resource for Worker revalidation.
Helper convenience methods wait locally, but the server still uses short asynchronous submit/status requests. For durable caller-controlled orchestration, call `submit_generation`, persist its `operationId` and idempotency key, then call `get_generation` or `wait_for_generation`.
For character animation, pass the canvas session and asset label to `animate_character`. The helper submits asynchronously and returns the completed compact result containing the authoritative formal `resource` and `asset`; do not synthesize a library asset from the first frame.
@@ -50,16 +50,15 @@ Every generation row requires a stable `Idempotency-Key` header and returns HTTP
| Capability | POST path | Required body fields | Common optional body fields |
| --- | --- | --- | --- |
| Image generation | `/api/external/v1/editor/images/generations` | `prompt` | `kind`, `style`, `model`, `aspectRatio`, `imageSize`, `size`, `referenceImageSrcs`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
| Image edit/redraw | `/api/external/v1/editor/images/edits` | `prompt`, `sourceReferenceId` | `referenceImageSrcs`, `model`, `size`, `projectId`, `assetFolderId`, `assetLabel`, `targetLayerId`, `canvasCompletion` |
| Background removal | `/api/external/v1/editor/images/background-removals` | `sourceImageSrc` | `projectId`, `sourceResourceId`, `targetLayerId`, static-image `assetKind`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
| Icon spritesheet | `/api/external/v1/editor/icon-spritesheets/generations` | `referenceId`, `iconDescriptions` | `sliceLayout`, `style`, `referenceImageSrcs`, `screenColor`, `model`, `aspectRatio`, `imageSize`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion` |
| Image edit/redraw | `/api/external/v1/editor/images/edits` | `prompt`, `sourceImageSrc` | `referenceImageSrcs`, `model`, `size`, `projectId`, `assetFolderId`, `assetLabel`, `sourceResourceId`, `targetLayerId`, `canvasCompletion` |
| Icon spritesheet | `/api/external/v1/editor/icon-spritesheets/generations` | `referenceImageSrc`, `iconDescriptions` | `style`, `referenceImageSrcs`, `screenColor`, `model`, `aspectRatio`, `imageSize`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion` |
| UI asset extraction | `/api/external/v1/editor/ui-designs/assets/extractions` | `sourceImageSrc`, `aspectRatio`, `imageSize` | `screenColor`, `model`, `referenceImageSrcs`, `projectId`, `assetFolderId`, `spritesheetLabel`, `canvasCompletion` |
| Character animation | `/api/external/v1/editor/character-animations/generations` | `sourceLayerId`, `sourceImageSrc`, `sourceWidth`, `sourceHeight`, `promptText`, `resolution`, `ratio`, `frameCount`, `durationSeconds`, `model` | `projectId`, `sourceResourceId`, `assetFolderId`, `assetLabel`, `canvasCompletion` |
| Video generation | `/api/external/v1/editor/videos/generations` | `prompt`, `model`, `aspectRatio`, `durationSeconds`, `resolution`, `mode`, `sound` | `referenceImageSrcs`, `referenceVideoSrcs`, `referenceAudioSrcs`, `webSearchEnabled`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion` |
| Sound effect | `/api/external/v1/editor/audios/sound-effects/generations` | `prompt` | `model`, `duration`, `loop`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
| Sound effect | `/api/external/v1/editor/audios/sound-effects/generations` | `prompt`, `duration` | `model`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
| Background music | `/api/external/v1/editor/audios/background-music/generations` | `gptDescriptionPrompt`, `makeInstrumental` | `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
Poll all nine through:
Poll all eight through:
```text
GET /api/external/v1/generations/{operationId}
@@ -73,35 +72,29 @@ Supply the `operationId` returned by submission. Poll no faster than `pollAfterM
- Pass `assetFolderId` plus `assetLabel` for image, edit, icon spritesheet, video, sound effect, and BGM operations when supported.
- UI extraction uses `assetFolderId` and `spritesheetLabel`.
- Character animation accepts `assetFolderId` and `assetLabel`. Its completed compact result directly returns the final `assetKind="character-animation"` resource and asset with `imageSequenceFrames` and `imageSequenceDurationMs`; never create a duplicate first-frame resource or asset.
- Background removal derives the final static-image `assetKind` from the authoritative source record. A conflicting request kind or any video, audio, animation, or image-sequence kind returns `400` before queueing. Without `canvasCompletion`, `targetLayerId` must point to the same authoritative object as `sourceImageSrc` (prefer `assetObjectId`, otherwise canonical bucket/object key).
- If a caller must manually create a `character-animation` resource or asset, put the authoritative frames and total sequence duration in `imageSequenceFrames` and `imageSequenceDurationMs`. Keep `generationInputs` replayable: it must not contain legacy runtime fields such as `characterAnimation`, `frames`, `previewVideoPath`, `frameCount`, `fps`, or `durationSeconds`.
- Reload project/library state after completion when full current state is required.
## Reference Field Mapping
After confirming a local upload, pass its stable `objectKey` into operations that accept object references:
After confirming a local upload, pass its stable `objectKey` into:
| Target capability | Field |
| --- | --- |
| Image generation | `referenceImageSrcs` |
| Image edit/redraw | `sourceReferenceId` must be a registered project resource ID or asset ID; additional references remain in `referenceImageSrcs` |
| Icon spritesheet | Register the primary spec as an `assetKind="icon-spec"` project resource or asset, then pass its returned ID as `referenceId`; additional style references remain in `referenceImageSrcs` |
| Image edit/redraw | `sourceImageSrc`; additional references in `referenceImageSrcs` |
| Icon spritesheet | `referenceImageSrc`; additional style references in `referenceImageSrcs` |
| UI design extraction | `sourceImageSrc`; additional references in `referenceImageSrcs` |
| Character animation | `sourceImageSrc` |
| Video with image references | `referenceImageSrcs` |
For image edit/redraw, confirming an upload is not sufficient: create a project resource or asset-library record first, then pass that record's ID as `sourceReferenceId`. The main source never accepts objectKey, URL, Data URL, or Blob URL. Use video/audio reference arrays only with models that support them. Do not pass an expiring signed read URL as a generation reference.
The icon-spritesheet primary `referenceId` is intentionally stricter than ordinary image references: it accepts only a current-owner project resource ID or asset ID whose authoritative `assetKind` is `icon-spec`. It does not accept an `objectKey`, URL, Data URL, or Blob URL.
`sliceLayout: "grid-2x2"` is an opt-in contract for four fixed game-runtime assets. The provider prompt and server persistence both preserve the ordered slots left-top, right-top, left-bottom, right-bottom. Omit it to retain the default connected-component slicing behaviour for ordinary free-form icon sheets.
Use video/audio reference arrays only with models that support them. Do not pass an expiring signed read URL as a generation reference.
## Common Values
Use OpenAPI as the final authority; these common values are a routing aid:
- Image `kind`: `spec`, `character`, `quick-edit`, `ui-design`, `publication-material`; ordinary image generation may omit it.
- External v1 currently has no structured game-scene generation operation. Do not send `kind: "scene"` or `assetKind: "scene"` through generic image generation; the server rejects both before queueing.
- Image `model`: `gpt-image-2`, `gemini-3.1-flash-image-preview`, `nanobanana2`, `nano-banana`.
- Image `aspectRatio`: `1:1`, `2:3`, `3:2`, `9:16`, `16:9`.
- Image `imageSize`: `0.5K`, `1K`, `2K`.
@@ -109,7 +102,6 @@ Use OpenAPI as the final authority; these common values are a routing aid:
- Video `aspectRatio`: `16:9`, `9:16`, `1:1`, `4:3`, `3:4`, `21:9`.
- Video `resolution`: `480p`, `720p`, `1080p`; `mode`: `std`; `sound`: `on` or `off`.
- Character animation uses `model: "seedance2.0-fast"`; `resolution`: `480p` or `720p`; `frameCount`: `32`, `40`, or `48`; `durationSeconds`: `4`, `5`, or `6`; `ratio`: `same`, `1:1`, `4:3`, `16:9`, `9:16`, or `3:4`.
- Sound effect uses canonical model `eleven_text_to_sound_v2`; omit `duration` or send `null` for automatic duration, otherwise send a finite `0.5-30` number. `loop` defaults to `false` and remains independent from Prompt text.
- UI extraction uses `aspectRatio: "1:1"`; use `imageSize: "1K"` for normal/small extraction and `2K` for dense designs.
Do not hard-code this list as a replacement client schema. In particular, the top-level image `style` field is intentionally extensible; see `requests-and-outputs.md` for its fallback behavior.
@@ -47,7 +47,6 @@ Infer what is already clear and ask only for missing fields that block the selec
| --- | --- |
| Generate a background, character, spec, UI mockup, or publication image | Image generation |
| Redraw, retouch, or replace an existing image | Image edit |
| Remove the background from an existing image | Background removal |
| Generate from a local reference | Upload and confirm the local file, then image generation or edit |
| Build a reusable transparent icon/game atlas from a visual spec | Icon spritesheet generation |
| Extract marked assets from an existing UI design | UI design asset extraction |
@@ -79,9 +78,9 @@ Keep the existing autonomous-build task graph. Do not add a parallel task system
1. `art-director` generates `assets/art-spec.png` with image generation, `kind: "spec"`, then registers it as `assetKind: "icon-spec"`. This image is the authoritative visual spec; `generationInputs.artSpec` is supporting structured context.
2. `design-foundation` generates `assets/ui-prototype.png` with `kind: "ui-design"`, using the registered art-spec resource ID in `referenceImageSrcs`.
3. `art-asset-plan` generates transparent `assets/art-spritesheet.png` through icon spritesheet generation, using the same registered art-spec resource ID as `referenceId` plus concrete `iconDescriptions`. For the four-category game contract it must also send `sliceLayout: "grid-2x2"`; this is an explicit fixed-slot contract, not a client-side guessed crop.
3. `art-asset-plan` generates transparent `assets/art-spritesheet.png` through icon spritesheet generation, using the same registered art-spec resource ID as `referenceImageSrc` plus concrete `iconDescriptions`.
For a playable Canvas game, do not stop at generation. Make `code-prototype` depend on `art-asset-plan` and consume the persisted `iconImageSrcs` slices for core players, blocks or targets, scene obstacles, and feedback. For the four-category game-chat contract, require response `sliceLayout: "grid-2x2"` and exactly four slices before registering the local runtime sheet; both fewer and extra components fail closed. Treat `art-spec.png` as reference-only. A full-sheet `<img>`, CSS background, path-only mention, guessed equal-grid crop, or code-drawn replacement for core entities is not runtime asset use. If slicing produces `sliceWarning`, keep the complete transparent sheet as a valid editor artifact, but fail the playable game asset gate until real slice files or verified atlas coordinates exist; never invent coordinates or replace the icon-spritesheet route with ordinary image generation.
For a playable Canvas game, do not stop at generation. Make `code-prototype` depend on `art-asset-plan` and consume the persisted `iconImageSrcs` slices for core players, blocks or targets, scene obstacles, and feedback. For the four-category game-chat contract, require exactly four slices before registering the local runtime sheet; both fewer and extra components fail closed. Treat `art-spec.png` as reference-only. A full-sheet `<img>`, CSS background, path-only mention, guessed equal-grid crop, or code-drawn replacement for core entities is not runtime asset use. If slicing produces `sliceWarning`, keep the complete transparent sheet as a valid editor artifact, but fail the playable game asset gate until real slice files or verified atlas coordinates exist; never invent coordinates or replace the icon-spritesheet route with ordinary image generation.
Never use `assets/ui-prototype.png` as the spritesheet visual-spec reference. UI extraction is outside this canonical DAG.
@@ -15,7 +15,7 @@ Use this reference to build generation payloads, carry canvas/library context, p
## Asynchronous Submission
All nine generation POST routes require `Idempotency-Key` and return HTTP `202` with an `ExternalEditorGenerationSubmissionResponse` shaped like:
All eight generation POST routes require `Idempotency-Key` and return HTTP `202` with an `ExternalEditorGenerationSubmissionResponse` shaped like:
```json
{
@@ -71,8 +71,6 @@ status = client.get_generation(operation_id)
completed = client.wait_for_generation(operation_id)
```
Background removal uses the same submission and polling state machine. `sourceImageSrc` must be a stable owner-scoped object key, project resource ID, or asset ID; never pass a Data URL, Blob URL, or expiring signed URL. An explicit resource ID or asset ID is resolved before any object-key fallback. If a raw object key has multiple registrations with conflicting authoritative metadata, pass `sourceResourceId` to disambiguate or the server returns `400`. Use `projectId + canvasCompletion` for normal canvas placement. When `canvasCompletion` is absent, `projectId + targetLayerId` replaces an existing resource-backed layer and is rejected before queueing if the target is invalid; for a raw object key, the target resource becomes the durable source binding rechecked by the Worker. If both placement fields are absent, the server does not add the result to the canvas. The completed compact result contains the stable output object key, dimensions, and persisted resource/asset references when requested.
## Canvas and Asset-Library Completion
For endpoints that support these fields, include:
@@ -98,9 +96,7 @@ A minimal `canvasCompletion` is:
}
```
`dialogId` is optional. The placeholder supplies canvas placement and completion coordinates; it is not a final media pixel-size constraint. For successful pixel-art snapping, the result layer uses the final logical-grid PNG dimensions even when they differ from the placeholder. Do not reconstruct canvas state from completion results. Reload the project and asset library when complete authoritative snapshots are needed.
Background removal preserves the source image dimensions. For normal canvas placement, the Python helper therefore requires the real `source_width` and `source_height` whenever `canvasSession` is used without an explicit `canvasWidth` plus `canvasHeight`; it never substitutes a square default. Passing `targetLayerId` instead selects in-place replacement, so the helper keeps the session's project/library fields without injecting `canvasCompletion` and rejects callers that explicitly combine both placement modes. The request `assetKind` is optional, static-image only, and must equal the authoritative source type when one exists. An in-place target must resolve to the same authoritative source object; a raw object key is bound to that target resource instead of relying on project-list order.
`dialogId` is optional. Do not reconstruct canvas state from completion results. Reload the project and asset library when complete authoritative snapshots are needed.
Character animation accepts `assetFolderId` and `assetLabel` and persists the final transparent sequence directly. Its completed compact result includes the authoritative `assetKind="character-animation"` resource and asset with `imageSequenceFrames` and `imageSequenceDurationMs`. Use those records directly and never synthesize a duplicate asset from the first frame.
@@ -108,8 +104,6 @@ For the lower-level asset/resource creation endpoints, `generationInputs` is rep
## Art Spec and Image Request
Generic External v1 image generation does not expose the main-site structured game-scene contract. `kind: "scene"` and `assetKind: "scene"` are both invalid and return HTTP `400` before any generation job is queued. Do not replace the structured scene fields and server-owned prompt assembly with a generic image prompt.
Carry the current art spec in `generationInputs.artSpec` and reflect important constraints in the prompt:
```json
@@ -149,7 +143,7 @@ Carry the current art spec in `generationInputs.artSpec` and reflect important c
The top-level `style` field is not the art spec's visual-style prose. It appends a short server-side clause to the prompt sent to the provider and enables deterministic post-processing:
- Omitted, `null`, empty string, or `"none"`: no clause is appended and no post-processing runs, without warning.
- `"pixelArt"`: append one short pixel-art line to the end of the prompt sent to the provider, and enable pixel-art snapping, for ordinary image generation, `kind: "character"`, and icon spritesheet generation. On successful snapping, each detected grid cell becomes one output pixel and the logical-grid PNG is persisted directly; it is not resized back to `size`, `imageSize`, the provider image, or `canvasCompletion.placeholder`. The line is appended, not substituted — the rest of your prompt is unchanged. For the exact per-kind wording, read the `style` field description in the OpenAPI document; it is the contract, and this guide deliberately does not copy it.
- `"pixelArt"`: append one short pixel-art line to the end of the prompt sent to the provider, and enable pixel-art snapping, for ordinary image generation, `kind: "character"`, and icon spritesheet generation. The line is appended, not substituted — the rest of your prompt is unchanged. For the exact per-kind wording, read the `style` field description in the OpenAPI document; it is the contract, and this guide deliberately does not copy it.
- Unknown strings, or `"pixelArt"` on unsupported kinds such as `spec`, `quick-edit`, `ui-design`, or `publication-material`: continue without style processing and return `warning.code: "unsupported-image-style"`.
- Non-string JSON values: malformed request, HTTP `400`.
@@ -173,10 +167,6 @@ client.generate_image(
)
```
Image edit/redraw has a stricter main-source identity rule. After upload confirmation, create either a project resource or an asset-library record and pass its `resourceId` or `assetId` as `sourceReferenceId`. Do not pass the uploaded objectKey as the main source; objectKey remains valid only for auxiliary `referenceImageSrcs` where the OpenAPI permits it.
Icon spritesheet generation has a stricter primary-spec contract. After upload confirmation, create a project resource or asset record with `assetKind: "icon-spec"`, retain its returned `resourceId` or `assetId`, and pass that ID as `referenceId`. The primary spec does not accept the uploaded `objectKey` directly; only additional style references may continue to use stable object keys in `referenceImageSrcs`.
For character animation from a local-only source, use actual dimensions and a stable synthetic layer ID:
```json
@@ -202,8 +192,7 @@ Do not guess dimensions or pass a temporary signed read URL. See `authentication
The completed `result` may contain stable artifact fields such as:
- `objectKey`, media type, dimensions, or task ID. For successful `pixelArt`, image `width`/`height`, icon `spritesheetWidth`/`spritesheetHeight`, and nested resource/asset dimensions are the actual final logical-grid PNG dimensions rather than requested, provider, or placeholder dimensions.
- Sound-effect `durationSeconds` is the probed MP3 duration and `loop` is the frozen request boolean; neither is inferred from Prompt text.
- `objectKey`, media type, dimensions, or task ID.
- `resource`, `resourceId`, or equivalent canvas reference.
- `asset`, `assetId`, or equivalent library reference.
- `spritesheetResource`, `spritesheetAsset`, and stable spritesheet metadata.
@@ -215,10 +204,6 @@ It deliberately excludes a complete project/canvas/library snapshot, Data URL, B
Interpret warnings only after the query reaches `status=completed`. The query-level `warning` is display-ready text. Compact `result.warning` and `result.sliceWarning` preserve structured artifact semantics.
### Delivery-size normalization result before pixel-art snapping
`result.warning.code: "dimension-restore-fallback"` records only the delivery-size normalization result established before any subsequent `pixelArt` snapping: the provider image could not be safely normalized, so its dimensions were preserved at that processing boundary. It does not describe or constrain the dimensions after `pixelArt`; if snapping succeeds, use the completed result's actual logical-grid dimensions as authoritative.
### Source-preserved post-processing failure
When `result.warning.code` is `postprocess-failed-source-preserved`:
@@ -548,80 +548,22 @@ class GenarrativeExternalClient:
idempotency_key=idempotency_key,
)
def edit_image(self, prompt: str, source_reference_id: str, **fields: Any) -> Any:
source_reference_id = source_reference_id.strip()
if not source_reference_id:
raise GenarrativeApiError("source_reference_id must be a registered resource or asset ID")
def edit_image(self, prompt: str, source_image_src: str, **fields: Any) -> Any:
self._apply_canvas_session_fields(fields, prompt, 1024, 1024)
prompt = self._apply_art_spec(fields, prompt)
idempotency_key = fields.pop("idempotencyKey", None)
return self.submit_and_wait_generation(
"/api/external/v1/editor/images/edits",
{"prompt": prompt, "sourceReferenceId": source_reference_id, **fields},
idempotency_key=idempotency_key,
)
def remove_background(
self,
source_image_src: str,
source_width: int | None = None,
source_height: int | None = None,
**fields: Any,
) -> Any:
source_image_src = normalize_optional_text(source_image_src)
if not source_image_src:
raise GenarrativeApiError(
"source_image_src must be an owner-scoped object key, resource ID, or asset ID"
)
if (source_width is None) != (source_height is None):
raise GenarrativeApiError("source_width and source_height must be provided together")
if source_width is not None and (
source_width <= 0 or source_height is None or source_height <= 0
):
raise GenarrativeApiError("source_width and source_height must be positive integers")
session = fields.get("canvasSession")
if session is None:
session = fields.get("canvas_session")
target_layer_id = normalize_optional_text(fields.get("targetLayerId"))
if target_layer_id and fields.get("canvasCompletion") is not None:
raise GenarrativeApiError(
"targetLayerId and canvasCompletion are mutually exclusive for background removal"
)
canvas_width = fields.get("canvasWidth")
canvas_height = fields.get("canvasHeight")
if (canvas_width is None) != (canvas_height is None):
raise GenarrativeApiError("canvasWidth and canvasHeight must be provided together")
if session is not None and canvas_width is None and not target_layer_id:
if source_width is None or source_height is None:
raise GenarrativeApiError(
"remove_background requires source_width and source_height when canvasSession is used without canvasWidth/canvasHeight"
)
fields["canvasWidth"] = source_width
fields["canvasHeight"] = source_height
self._apply_canvas_session_fields(
fields,
fields.get("assetLabel", "去背景结果"),
source_width or 1,
source_height or 1,
)
if target_layer_id:
fields.pop("canvasCompletion", None)
idempotency_key = fields.pop("idempotencyKey", None)
return self.submit_and_wait_generation(
"/api/external/v1/editor/images/background-removals",
{"sourceImageSrc": source_image_src, **fields},
{"prompt": prompt, "sourceImageSrc": source_image_src, **fields},
idempotency_key=idempotency_key,
)
def generate_icon_spritesheet(
self,
reference_id: str,
reference_image_src: str,
icon_descriptions: list[str],
**fields: Any,
) -> Any:
reference_id = normalize_optional_text(reference_id)
if not reference_id:
raise GenarrativeApiError("reference_id must be a registered icon-spec resource or asset ID")
descriptions = [item.strip() for item in icon_descriptions if item.strip()]
if not descriptions:
raise GenarrativeApiError("icon_descriptions must contain at least one non-empty item")
@@ -632,9 +574,9 @@ class GenarrativeExternalClient:
return self.submit_and_wait_generation(
"/api/external/v1/editor/icon-spritesheets/generations",
{
**fields,
"referenceId": reference_id,
"referenceImageSrc": reference_image_src,
"iconDescriptions": descriptions,
**fields,
},
idempotency_key=idempotency_key,
)
@@ -707,19 +649,13 @@ class GenarrativeExternalClient:
idempotency_key=idempotency_key,
)
def generate_sound_effect(
self,
prompt: str,
duration: float | None = None,
loop: bool = False,
**fields: Any,
) -> Any:
def generate_sound_effect(self, prompt: str, duration: int, **fields: Any) -> Any:
self._apply_canvas_session_fields(fields, prompt, 360, 120)
prompt = self._apply_art_spec(fields, prompt)
idempotency_key = fields.pop("idempotencyKey", None)
return self.submit_and_wait_generation(
"/api/external/v1/editor/audios/sound-effects/generations",
{"prompt": prompt, "duration": duration, "loop": loop, **fields},
{"prompt": prompt, "duration": duration, **fields},
idempotency_key=idempotency_key,
)
@@ -739,14 +675,11 @@ def _self_test() -> None:
b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR"
b"\x00\x00\x00\x02\x00\x00\x00\x03\x08\x06\x00\x00\x00"
)
with tempfile.NamedTemporaryFile(suffix="Hero Image.png", delete=False) as fh:
with tempfile.NamedTemporaryFile(suffix="Hero Image.png") as fh:
fh.write(png)
temp_path = fh.name
try:
assert image_dimensions(temp_path) == (2, 3)
assert source_layer_id_from_path(temp_path).startswith("external-reference-")
finally:
Path(temp_path).unlink(missing_ok=True)
fh.flush()
assert image_dimensions(fh.name) == (2, 3)
assert source_layer_id_from_path(fh.name).startswith("external-reference-")
assert unwrap_envelope({"ok": True, "data": {"upload": 1}}) == {"upload": 1}
client = GenarrativeExternalClient(api_key="test")
session = {"projectId": "proj-demo", "assetFolderId": "editor-asset-folder-demo"}
@@ -828,64 +761,14 @@ def _self_test() -> None:
assert len(result["asset"]["imageSequenceFrames"]) == 2
assert result["asset"]["imageSequenceDurationMs"] == 4000
calls.clear()
background_result = client.remove_background(
"uploads/source.png",
720,
1280,
canvasSession=session,
assetLabel="去背景结果",
)
assert background_result["taskId"] == "task-demo"
assert calls[0]["path"] == "/api/external/v1/editor/images/background-removals"
assert calls[0]["body"]["sourceImageSrc"] == "uploads/source.png"
assert calls[0]["body"]["projectId"] == "proj-demo"
assert calls[0]["body"]["assetFolderId"] == "editor-asset-folder-demo"
assert calls[0]["body"]["assetLabel"] == "去背景结果"
assert calls[0]["body"]["canvasCompletion"]["title"] == "去背景结果"
assert calls[0]["body"]["canvasCompletion"]["placeholder"]["width"] == 720
assert calls[0]["body"]["canvasCompletion"]["placeholder"]["height"] == 1280
calls.clear()
client.remove_background(
"uploads/source.png",
canvasSession=session,
targetLayerId="layer-1",
assetLabel="原位去背景结果",
)
assert calls[0]["body"]["projectId"] == "proj-demo"
assert calls[0]["body"]["assetFolderId"] == "editor-asset-folder-demo"
assert calls[0]["body"]["assetLabel"] == "原位去背景结果"
assert calls[0]["body"]["targetLayerId"] == "layer-1"
assert "canvasCompletion" not in calls[0]["body"]
calls.clear()
try:
client.remove_background(
"uploads/source.png",
canvasSession=session,
targetLayerId="layer-1",
canvasCompletion={"title": "冲突完成指令"},
)
except GenarrativeApiError as error:
assert "targetLayerId and canvasCompletion are mutually exclusive" in str(error)
else:
raise AssertionError("background removal must reject conflicting canvas placement modes")
assert calls == []
try:
client.remove_background("uploads/source.png", canvasSession=session)
except GenarrativeApiError as error:
assert "source_width and source_height" in str(error)
else:
raise AssertionError("canvas background removal must not guess source dimensions")
assert calls == []
client.generate_icon_spritesheet(
"editor-resource-spec",
["蛇头向上", "蛇身直线", "转角", "尾部", "四类食物"],
canvasSession=session,
assetLabel="贪吃蛇透明图集",
referenceId="must-not-override-explicit-reference",
iconDescriptions=["不得覆盖显式图标描述"],
)
assert calls[0]["path"] == "/api/external/v1/editor/icon-spritesheets/generations"
assert calls[0]["body"]["referenceId"] == "editor-resource-spec"
assert calls[0]["body"]["referenceImageSrc"] == "editor-resource-spec"
assert calls[0]["body"]["screenColor"] == "auto"
assert calls[0]["body"]["iconDescriptions"][0] == "蛇头向上"
assert calls[1]["path"] == "/api/external/v1/generations/task-operation-demo"
@@ -3,7 +3,7 @@ name: genarrative-play-type-integration
description: 在 Genarrative 中新增或补齐一个创作入口/玩法类型时,按入口配置、前端分流、契约、后端接口、工作台、独立生成页、结果页、发布、统一作品详情、正式 runtime、公开 read model、基础统计与作品架/广场的顺序接入。
license: MIT
metadata:
author: Genarrative Team
author: Hermes Agent
version: "1.0"
---
@@ -90,14 +90,13 @@ metadata:
12. **旧数据策略**:旧草稿、旧发布配置、旧分享码是迁移、降级展示、重新生成,还是明确不兼容。
- `AGENTS.md`
- `docs/project-memory/shared-memory/`
- `.hermes/shared-memory/`
- `CONTEXT.md`
- `docs/README.md`
- `docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`
- 相关玩法 PRD 或设计文档
- `.codex/skills/genarrative-play-type-integration/references/genarrative-analytics-tracking-runtime.md`(涉及正式 runtime 埋点时)
如果文档不能精确指导字段、契约、资产槽位、生成流程和恢复语义,先补文档再编码。新增长期约定时同步 `docs/project-memory/shared-memory/`
如果文档不能精确指导字段、契约、资产槽位、生成流程和恢复语义,先补文档再编码。新增长期约定时同步 `.hermes/shared-memory/`
### 2. 定玩法边界
@@ -1,126 +0,0 @@
---
name: genarrative-spacetimedb
description: Genarrative 的 SpacetimeDB 项目适配规范。用于涉及 SpacetimeDB 架构、Rust module、schema、migration、reducer、procedure、view、绑定生成、CLI、MCP、发布、调试或运行时核验的任务。
---
# Genarrative SpacetimeDB 项目指导
本 skill 只保存 Genarrative 的项目约束和操作边界;SpacetimeDB 的通用 API、语言 SDK 和 CLI 手册由已安装的官方插件提供。项目规则覆盖插件示例中的默认值或与本仓库冲突的建议。
## 官方插件依赖
开始 SpacetimeDB 任务时,按任务范围读取官方插件 skill:
- `spacetimedb:concepts`:核心语义、表、reducer、procedure、view、订阅和身份。
- `spacetimedb:rust-server`Rust module、表属性、访问器、迁移兼容性和 SDK API。
- `spacetimedb:cli`:初始化、构建、发布、生成绑定、SQL、调用、日志和 server 管理。
- `spacetimedb:typescript-client`:前端生成绑定、订阅和 TypeScript 客户端 SDK;其它语言客户端按需读取插件对应 skill。
- `spacetimedb:mcp`:通过已连接的 MCP 操作运行中的数据库;没有 MCP 工具时使用 CLI 等价命令。
如果当前环境尚未安装插件,使用:
```bash
codex plugin marketplace add clockworklabs/SpacetimeDB --sparse .agents --sparse codex-plugin
codex plugin add spacetimedb\@spacetimedb-plugins
```
插件不可用时,以当前源码、`docs/`、生成绑定和仓库脚本为准,不凭记忆发明 SpacetimeDB API。
## 架构边界
Genarrative 的唯一有效后端路线是:
```text
server-rs + Axum + SpacetimeDB
```
- `module-*`:领域模型、命令、应用规则、领域事件和领域错误;不得直接依赖 Axum、SpacetimeDB table/reducer/procedure、`spacetime-client`、外部平台或文件系统。
- `spacetime-module`SpacetimeDB 表、reducer、procedure、view、migration、事务 adapter 和 row mapper。
- `spacetime-client`:后端访问 SpacetimeDB 的 typed facade;其它后端 crate 不直接创建第二套访问路径。
- `api-server`HTTP、SSE、BFF 和外部副作用编排。
- `platform-*`:OSS、LLM、认证、语音等外部平台能力。
- `shared-contracts` / `packages/shared`:前后端 DTO、公开契约和无业务真相的共享 TypeScript 代码。
- 前端只负责表现、交互、临时 UI 状态和后端结果渲染,不绕过 BFF/投影直接读取私有表或推导正式业务状态。
SpacetimeDB 是数据和事务层,不替代 `api-server` BFF、`spacetime-client` facade 或公开 read model。插件提供的“SpacetimeDB 可替代传统服务端”通用描述不能改变本项目边界。
## 语义与安全不变量
- Reducer 是原子事务写路径,不向调用者返回业务数据;读取通过订阅、read model、view 或 BFF。
- Reducer 必须确定性执行:不得访问文件系统、网络、系统时钟或外部随机源;使用 `ctx.timestamp``ctx.rng()` / `ctx.random()` 等 SpacetimeDB 能力。
- 授权使用上下文中的 `ctx.sender()`(或当前语言对应 API),不信任调用参数传入的身份。
- Auto-increment ID 不是排序依据;需要顺序时使用时间戳或显式序列字段。
- Private table 是后端事实;用户可见状态通过 BFF、投影或明确的 public table/view 暴露。公共表仍只能由 reducer/procedure 写入。
- Procedure 在 2.8 已稳定,可使用显式事务和 `ctx.http`Genarrative 默认仍把外部 provider 协议放在 `platform-*`,把编排放在 `api-server`,除非当前架构明确要求 module procedure。
- Event table 必须显式订阅,按插入事件消费;不要依赖其持久化行或 `OnUpdate`。需要更新回调时使用持久表或带主键的 procedural view。
- Standalone MCP 是 operator/developer 集成面,不是 BFF、facade 或公开 read model 的替代品。MCP/SQL/CLI 的写入都必须有明确授权;日常 smoke 优先只读。
## Schema 与迁移
修改现有 SpacetimeDB persistent table 时:
1. 新字段只能追加到 Rust 表结构体末尾,并设置明确的 `#[default(...)]`
2. 删除、改名、重排、改类型或破坏性约束变更前,必须先询问用户并确认迁移计划。
3. 同步更新 `server-rs/crates/spacetime-module/src/migration.rs`、后端架构文档中的表目录、生成绑定和相关契约/测试。
4. 运行:
```bash
npm run spacetime:generate
npm run check:spacetime-schema
```
Event table 的较宽松自动迁移规则不适用于 persistent table,不能借此绕过上述门禁。以当前源码和 `docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md` 为 schema 真相。
## CLI、目标 server 与本地开发
- 优先使用仓库 wrapper`npm run dev:spacetime`、`npm run dev:api-server`、`npm run spacetime:generate`。
- 直接使用 CLI 时始终显式传 `--server` 或 `--server-url`;不要依赖默认云端目标或个人 CLI 默认 server。
- 不新增 `maincloud` / `MAINCLOUD` 命令、环境变量、脚本或文档;历史残留只按历史处理。
- 人工命令、本地联调、排障步骤和文档示例禁止使用 `spacetime --root-dir`;本地数据隔离使用项目脚本或 `--data-dir`。
- `spacetime publish` 的 `--delete-data=always` 只在明确授权的破坏性操作中使用;schema 冲突优先按项目脚本和受控迁移流程处理。
- 项目 SpacetimeDB crate、SDK、CLI/standalone 和生成 bindings 按 `2.8.3` 对齐;官方发行资产、Rust crates 和容器镜像使用 `v2.8.3` 版本标签,仓库额外固定 CLI commit `8e410d2842147bd8e5a32a9589cc00c19f7478e2`。升级时核对 Cargo 精确 pin、实际 CLI 和运行中服务二进制,不把本地 CLI 重装当作仓库升级。
本地开发默认由项目启动器管理端口;实际监听地址以 `.app/dev-stack.json` 和启动日志为准,不能从文档默认端口推断当前目标。发布后确认 api-server 使用的是同一 database、server 和 token。
## MCP 与运行时核验
如果当前会话暴露 SpacetimeDB MCP 工具,读取运行中的数据库优先使用 typed MCP:先 `list_databases` / `get_schema`,再做只读 SQL 或 `ping`;调用 reducer 或 SQL 写入前确认目标、身份和授权。没有 MCP 工具时使用显式目标的 CLI。2.8 standalone 的 MCP HTTP endpoint 是 `POST /v1/database/{name_or_identity}/mcp`,提供 `ping`、`get_schema`、`sql`、`call`;升级 smoke 在隔离数据库中只做 `initialize`、`tools/list`、`ping`、`get_schema`,除非写入明确属于任务范围。
排查“服务健康但业务不可用”时按顺序核对:
1. SpacetimeDB standalone 是否运行(本地优先 `npm run dev:spacetime`,主机侧核对 systemd)。
2. module 是否发布到 api-server 实际使用的同一个 server/database。
3. 生成绑定是否来自当前 module。
4. api-server 的 database、server URL 和 token 是否一致。
5. reducer/procedure 是否真正被调用;区分超时、权限、schema 不存在和业务错误。
6. `/healthz` / `/readyz` 通过但业务仍失败时,继续检查 API 日志和公开路由,不把健康检查当作业务成功证明。
主机升级需核对运行中进程而非只看 PATH:
```bash
type -a spacetime
spacetime --version
pid="$(systemctl show spacetimedb.service -p MainPID --value)"
readlink -f "/proc/${pid}/exe"
"/proc/${pid}/exe" --version
curl -fsS http://127.0.0.1:3101/v1/ping
```
## 修改后的最小验证
按范围执行定向测试/类型检查,并至少运行:
```bash
npm run check:encoding
git diff --check
```
涉及 schema 时追加 `npm run spacetime:generate` 和 `npm run check:spacetime-schema`;涉及 API 时按当前后端文档启动 `npm run dev:api-server` 并检查 `/healthz`。无法运行的验证要在交付说明中标记为未验证并说明原因。
## 参考入口
- `docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`
- `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
- `server-rs/README.md`
- `scripts/check-spacetime-schema-guard.mjs`
- `scripts/check-server-rs-ddd-boundaries.mjs`
+178
View File
@@ -0,0 +1,178 @@
---
name: spacetimedb-cli
description: SpacetimeDB 2.7 CLI reference for Genarrative. Use for spacetime build, publish, generate, call, sql, logs, server management, local dev, explicit server targeting, version checks, and remote runtime verification.
---
# SpacetimeDB CLI
Use this skill when working with the `spacetime` CLI in Genarrative. Prefer repository scripts when they exist, and keep every operation pinned to an explicit target server or local process.
## Genarrative Rules
- Do not rely on the default SpacetimeDB cloud target. Pass `--server` or `--server-url` explicitly in scripts, docs, smoke tests, and manual troubleshooting.
- Do not introduce `maincloud` / `MAINCLOUD` commands, env vars, or docs. Treat old references as historical residue.
- Do not use `spacetime --root-dir` in manual commands or docs. Use project scripts, `--data-dir`, explicit `--server`, or the configured running service.
- For repository version upgrades, update `server-rs/Cargo.toml` exact pins, regenerate bindings, and verify the actual CLI/runtime version. Do not treat a local CLI reinstall as a repo upgrade.
- For host upgrades, verify the running service binary, not just shell PATH: `systemctl show ... MainPID` -> `/proc/$pid/exe --version` -> `/v1/ping`.
## Core Commands
```bash
# Build module
spacetime build
spacetime build --debug
# Publish to an explicit server
spacetime publish my-database --server http://127.0.0.1:3101 --yes=migrate,break-clients
# Destructive publish only when explicitly intended
spacetime publish my-database --server http://127.0.0.1:3101 --delete-data=always --yes=delete-data,migrate
# Delete data only for breaking schema conflicts
spacetime publish my-database --server http://127.0.0.1:3101 --delete-data=on-conflict --yes=migrate
# Generate bindings
spacetime generate --lang typescript|csharp|rust|unrealcpp --out-dir ./bindings --module-path ./server
```
## Genarrative Local Workflow
```bash
# Prefer project wrappers
npm run dev:spacetime
npm run dev:api-server
npm run spacetime:generate
# Query local database
spacetime sql my-db --server http://127.0.0.1:3101 "SELECT * FROM players"
# Logs
spacetime logs my-db --server http://127.0.0.1:3101 -f
```
## Database Interaction
```bash
# SQL / describe
spacetime sql my-db --server http://127.0.0.1:3101 "SELECT * FROM users"
spacetime describe my-db --server http://127.0.0.1:3101 --json
spacetime describe my-db table users --server http://127.0.0.1:3101 --json
# Reducer/procedure calls. Arguments are positional JSON values.
spacetime call --server http://127.0.0.1:3101 my-db my_reducer '"value"' '123'
# 2.5+ accepts hex strings for Identity arguments without full JSON tuple syntax.
spacetime call --server http://127.0.0.1:3101 my-db reducer_needing_identity 0xabc123...
# Subscribe from CLI
spacetime subscribe my-db "SELECT * FROM users" --num-updates 10 --server http://127.0.0.1:3101
```
## Standalone MCP Endpoint (2.7)
SpacetimeDB 2.7 standalone exposes an authenticated JSON-RPC MCP endpoint at
`POST /v1/database/{name_or_identity}/mcp`. It advertises `ping`, `get_schema`,
`sql`, and `call`. The SQL and reducer tools execute with the bearer token's
identity, so keep routine smoke checks read-only.
```bash
curl -fsS \
-H "Authorization: Bearer ${SPACETIME_TOKEN}" \
-H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"genarrative-smoke","version":"1.0.0"}}}' \
http://127.0.0.1:3101/v1/database/my-db/mcp
curl -fsS \
-H "Authorization: Bearer ${SPACETIME_TOKEN}" \
-H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"ping","arguments":{"message":"genarrative"}}}' \
http://127.0.0.1:3101/v1/database/my-db/mcp
```
For repository upgrade validation, also call `tools/list` and the read-only
`get_schema` tool against an isolated local database. Do not use `sql` or `call`
for writes unless that mutation is explicitly in scope.
## Server & Auth
```bash
spacetime server list
spacetime server add local --url http://localhost:3000 --default
spacetime server add genarrative-dev --url http://127.0.0.1:3101
spacetime server ping genarrative-dev
spacetime login
spacetime login --token <token>
spacetime login show
spacetime logout
```
## Version & Runtime Verification
```bash
# CLI resolution can be misleading; compare all candidates when diagnosing.
type -a spacetime
spacetime --version
spacetime version list
# Verify a systemd service binary actually changed.
pid="$(systemctl show spacetimedb.service -p MainPID --value)"
readlink -f "/proc/${pid}/exe"
"/proc/${pid}/exe" --version
curl -fsS http://127.0.0.1:3101/v1/ping
```
## Flags
| Flag | Description |
|------|-------------|
| `--server`, `-s` | Target server nickname, host, or URL |
| `--yes`, `-y` | Non-interactive prompt skipping; in 2.6+ use scoped values |
| `--delete-data`, `-c` | Publish data policy: `always`, `on-conflict`, or `never` |
| `--module-path`, `-p` | Module project path |
| `--bin-path`, `-b` | Publish/generate from compiled wasm |
| `--no-config` | Ignore `spacetime.json` |
| `--env` | Select config file layering environment |
## Troubleshooting
### Not Logged In
```bash
spacetime login
```
### Server Not Responding
```bash
spacetime server ping <server>
curl -fsS http://127.0.0.1:3101/v1/ping
```
For local Genarrative work, start SpacetimeDB first with `npm run dev:spacetime`, then start `npm run dev:api-server`.
### Schema Conflict
```bash
spacetime publish my-db --server http://127.0.0.1:3101 --delete-data=on-conflict --yes=migrate
```
Use `--delete-data=always` only with explicit approval.
### Version Mismatch
```bash
rg -n 'spacetimedb' server-rs/Cargo.toml
spacetime --version
spacetime version list
pid="$(systemctl show spacetimedb.service -p MainPID --value)"
"/proc/${pid}/exe" --version
```
## Notes
- Procedure calls remain stable in 2.7; module HTTP handlers/webhooks and RLS capabilities still require their documented gates.
- 2.5 fixed `publish --delete-data` config fallback; 2.6 kept that behavior and improved CLI binary distribution; 2.7 adds `spacetime sql --format json` and database `lock` / `unlock`.
- The official 2.7.0 Linux release archives and container image currently use the `v2.7.0-hotfix3` asset tag while binaries report `2.7.0`; keep the asset tag distinct from the runtime version check.
- Do not assume `spacetime version install 2.7.0` selected hotfix3: stale updater metadata can install bare-tag commit `a08663c7...`. For the current release, verify CLI commit `d220349a...` and use the official hotfix3 archive or repository provision flow when it differs.
- Genarrative scripts should pass `--server` or `--server-url` explicitly instead of relying on CLI defaults.
+119
View File
@@ -0,0 +1,119 @@
---
name: spacetimedb-concepts
description: Understand SpacetimeDB 2.7 architecture, reducer/procedure/table/view semantics, schema evolution, subscriptions, identity, and Genarrative-specific backend boundaries. Use when designing or reviewing SpacetimeDB-backed features.
---
# SpacetimeDB Core Concepts
SpacetimeDB is a relational database that also executes application logic in uploaded modules. In Genarrative, it is the data and transaction layer behind `server-rs + Axum + SpacetimeDB`, not a replacement for the `api-server` BFF or external platform adapters.
## Genarrative Boundaries
- Domain rules live in `module-*`.
- SpacetimeDB tables, reducers, procedures, migrations, row mappers, and read models live in `spacetime-module`.
- Backend access goes through `spacetime-client` facades.
- HTTP/SSE/BFF and external orchestration stay in `api-server`.
- External side effects stay in `platform-*`.
- Frontend renders backend truth and must not bypass BFF/projections to invent formal business state.
## Critical Rules
1. **Reducers are transactional**: they do not return data to callers. Read through subscriptions, read models, views, or BFF endpoints.
2. **Reducers are deterministic**: no filesystem, network, wall-clock, or external RNG. Use `ctx.timestamp`, `ctx.rng()` / `ctx.random()`, and tables.
3. **Procedures are stable in 2.7**: they can use explicit transactions and outgoing HTTP via `ctx.http`.
4. **Identity comes from context**: use `ctx.sender()` or language equivalent for authorization. Never trust identity passed as an argument.
5. **Auto-increment IDs are not ordering guarantees**: gaps are normal. Use timestamps or explicit sequence columns for ordering.
6. **Schema changes need migration discipline**: existing Genarrative table fields must be appended with defaults; update migration code, table catalog, generated bindings, and run `npm run check:spacetime-schema`.
## Tables
- Private tables are the default; only reducers/procedures and database owners can access them.
- Public tables are exposed to clients through subscriptions. Writes still go through reducers/procedures.
- Organize data by access pattern when bandwidth or update frequency differs.
- Existing persistent tables in Genarrative are conservative: no rename, delete, reorder, or type changes without a user-approved migration plan.
## Reducers
Reducers are deterministic transactional functions. They are the primary client-invoked mutation path.
- No global mutable state.
- No filesystem, network, timers, or non-deterministic RNG.
- Return `Result<(), String>` for expected sender-visible errors.
- Use `ctx.sender()` for authorization.
- Store persistent state in tables.
## Procedures
Procedures are stable in 2.7. They can be scheduled, can open explicit transactions with `with_tx` / `try_with_tx`, and can use outgoing HTTP (`ctx.http`).
Genarrative default: keep external provider protocols in `platform-*` and orchestration in `api-server` unless a task explicitly moves a workflow into a module procedure.
Module HTTP handlers/webhooks and RLS `client_visibility_filter` remain subject to their documented gates in 2.7.
## Views
Views expose computed read-only data. SpacetimeDB 2.7 supports primary keys on procedural views in Rust, TypeScript, C#, and C++. Clients can receive update events when subscribed to such views with primary keys. Ensure the view never returns duplicate primary keys, because that can fail view refresh and roll back the triggering transaction.
## Event Tables
Event tables broadcast reducer/procedure-specific facts to subscribers and must be subscribed explicitly. They are excluded from `subscribe_to_all_tables()`.
Since 2.6, event tables support broader layout-altering automigrations, including column removal, reordering, and type changes that regular tables reject. This relaxed migration behavior is for event-only tables, not persistent tables.
Event-table primary keys and constraints are transaction-scoped. They can reject duplicate event rows within one transaction, but event rows are not retained in client cache, so clients observe event tables through insert callbacks only. Do not design Genarrative event tables around `OnUpdate` / `on_update` / `onUpdate`; use a persistent table or a primary-keyed procedural view when update callbacks are required.
Official 2.4.1 through 2.7 release notes document primary-key-backed update callbacks for procedural views, not event tables.
## Subscriptions
1. Subscribe to SQL queries or generated table/query builders.
2. Receive initial matching rows.
3. Receive updates when subscribed rows change.
4. Render from subscribed data, not reducer return values.
Best practices:
- Group subscriptions by lifetime.
- Subscribe to new data before unsubscribing old data during transitions.
- Avoid overlapping queries that duplicate row delivery.
- Use indexes for subscribed filters.
## Standalone MCP
SpacetimeDB 2.7 standalone exposes `POST /v1/database/{name_or_identity}/mcp`
using MCP JSON-RPC protocol `2025-06-18`. Its tools are `ping`, `get_schema`,
`sql`, and `call`; SQL and reducer calls run with the authenticated caller's
identity. In Genarrative this is an operator/developer integration surface, not
a replacement for `api-server` BFF routes, `spacetime-client` facades, or public
read models. Upgrade smoke should use an isolated local database and restrict
itself to `initialize`, `tools/list`, `ping`, and `get_schema` unless writes are
explicitly intended.
## 2.2.0 to 2.7.0 Delta
Genarrative introduced SpacetimeDB around 2.2.0. Important changes since then:
- **2.2.0**: v3 WebSocket transport and TS SDK default, safer production operations (`lock`/`unlock`, safer `delete`, better `publish --yes`), TS React `useProcedure`, table clearing APIs, empty-table drop automigration, primary-key migration fixes, bytes-key B-tree support, durability hardening.
- **2.3.0**: first-party Godot SDK, more WebSocket pipelining/batching, HTTP/2 backend support, Vue `useProcedure`, Unity 6 WebGL support, commitlog compression/throughput improvements, Rust `DbContext` generics, `ReducerContext::identity` deprecated in favor of `database_identity`, connection lifecycle and unsubscribe fixes.
- **2.4.0**: unstable module HTTP handlers/webhooks, faster synchronous WASM reducer runtime, commitlog resume truncation fix for silent data loss risk, better commitlog decode context, V8 heap metrics for procedure workers, JS execution-time billing regression reverted.
- **2.4.1**: Rust and TypeScript procedural views can declare primary keys, enabling `OnUpdate` events for subscribed views; fixed index schema from ST tables.
- **2.5.0**: procedures are stable, C# procedural views gain primary keys, event tables allow broader layout-altering automigrations, BTreeSet storage makes row insertion deterministic and avoids accidentally quadratic bulk insert behavior, `wasm_memory_bytes` billing metric semantics changed, template version constraints unified, `publish --delete-data` config fallback fixed, CLI `call` accepts hex Identity arguments.
- **2.6.0**: procedural-view primary keys are available across Rust, TypeScript, and C#, commitlog gains `max_segment_size` / `write_buffer_size` / `preallocate_segments`, the default write buffer increases for throughput, event-table automigrations improve, and CLI binary distribution expands.
- **2.6.1**: procedure contexts again receive the caller `Identity` and `ConnectionId`; generated TypeScript `Option<T>` fields use optional keys; `spacetime init --template` lists available templates when no template argument is supplied.
- **2.7.0**: existing tables can add unique or primary-key constraints when current data satisfies them; standalone exposes an authenticated database MCP endpoint; Rust adds context-capability and table-accessor traits; `spacetime sql --format json` and database locking are available; view cleanup, backing-table migration, connection metrics, and memory metrics improve. Official current release assets use the `v2.7.0-hotfix3` tag while binaries report `2.7.0`.
## Debugging Checklist
1. Is the Genarrative SpacetimeDB server running? Use `npm run dev:spacetime` locally or host-local `systemctl`.
2. Is the module published to the same server the API uses?
3. Are generated bindings current? Use `npm run spacetime:generate`.
4. Is `api-server` using the same database and token?
5. Is the reducer/procedure actually called?
6. Did `/healthz` / `/readyz` pass while business SpacetimeDB calls still timeout? Inspect API logs and public route behavior.
## Editing Behavior
- Make the smallest change necessary.
- Do not invent SpacetimeDB APIs; verify against current docs, generated bindings, or source.
- For Genarrative schema edits, update migration code, table catalog/docs, generated bindings, and relevant tests.
- After schema edits, run `npm run spacetime:generate` and `npm run check:spacetime-schema`.
+280
View File
@@ -0,0 +1,280 @@
---
name: spacetimedb-rust
description: Develop SpacetimeDB 2.7 server modules in Rust for Genarrative. Use when writing or reviewing tables, reducers, procedures, views, migrations, row mappers, schema changes, and module logic.
---
# SpacetimeDB Rust Module Development
Use this skill for Rust code in `server-rs/crates/spacetime-module` and related Genarrative schema/migration work.
## Genarrative Rules
- Keep domain rules in `module-*`; keep SpacetimeDB tables, reducers, procedures, views, mappers, and transaction adapters in `spacetime-module`.
- Existing table fields must be appended at the end with explicit defaults. Do not rename, remove, reorder, or change field types without a user-confirmed migration plan.
- After schema changes, update `migration.rs`, table catalog/docs, generated bindings, and run `npm run spacetime:generate` plus `npm run check:spacetime-schema`.
- Private tables are backend facts. Expose user-visible state through BFF endpoints/read models rather than direct client SQL.
## Hallucinated APIs: Do Not Use
```rust
#[derive(Table)] // Tables use #[table], not derive
#[derive(Reducer)] // Reducers use #[reducer], not derive
#[derive(SpacetimeType)] // Do not derive this on #[table] structs
pub fn reducer(ctx: &mut ReducerContext) {} // Use &ReducerContext
ctx.db.player // Use ctx.db.player()
ctx.db.player.find(id) // Use ctx.db.player().id().find(&id)
ctx.sender // Use ctx.sender()
ctx.db.user().name().update(..) // Update by primary key only
spacetimedb = { version = "...", features = ["unstable"] } // Not needed for procedures since 2.5
```
## Required Patterns
```rust
use spacetimedb::{reducer, table, Identity, ReducerContext, Table, Timestamp};
use spacetimedb::SpacetimeType; // Custom types only, not tables
#[table(accessor = player, public)]
pub struct Player {
#[primary_key]
#[auto_inc]
pub id: u64,
pub owner: Identity,
pub name: String,
pub created_at: Timestamp,
}
#[reducer]
pub fn create_player(ctx: &ReducerContext, name: String) -> Result<(), String> {
if name.trim().is_empty() {
return Err("name required".to_string());
}
ctx.db.player().try_insert(Player {
id: 0,
owner: ctx.sender(),
name,
created_at: ctx.timestamp,
})?;
Ok(())
}
```
Hard requirements:
- Import `Table` for table operations.
- Use `accessor = identifier`, not string literals.
- Use `ctx.sender()` for authorization.
- Use `ctx.rng()` / `ctx.random()` / `ctx.new_uuid_*()` for deterministic randomness and UUIDs.
- Use `Result<(), String>` for expected sender errors; avoid panics except impossible states.
- Use `try_insert()` in `Result` reducers when constraint violations should be reported cleanly.
## Tables
```rust
#[spacetimedb::table(accessor = game_tick_schedule, scheduled(game_tick))]
pub struct GameTickSchedule {
#[primary_key]
#[auto_inc]
pub scheduled_id: u64,
pub scheduled_at: ScheduleAt,
}
```
Table attributes:
| Attribute | Description |
|-----------|-------------|
| `accessor = identifier` | API name used in `ctx.db.{accessor}()` |
| `public` | Visible to clients via subscriptions |
| `event` | Transient event table |
| `scheduled(function_name)` | Schedule table that triggers a reducer/procedure |
| `index(accessor = idx, btree(columns = [a, b]))` | Multi-column index |
Column attributes:
| Attribute | Description |
|-----------|-------------|
| `#[primary_key]` | One primary key per table |
| `#[auto_inc]` | Auto-generates integer values when inserting `0` |
| `#[unique]` | Unique constraint and `find()` accessor |
| `#[index(btree)]` | B-tree index and `filter()` accessor |
| `#[default(...)]` | Required for new fields on existing Genarrative tables |
## Genarrative Schema Change Pattern
```rust
#[spacetimedb::table(accessor = creation_entry_config, public)]
pub struct CreationEntryConfig {
#[primary_key]
pub id: u64,
pub existing_field: String,
// Append new fields at the end and provide a default.
#[default(false)]
pub new_flag: bool,
}
```
Then update `migration.rs`, table catalog/docs, generated bindings, and run:
```bash
npm run spacetime:generate
npm run check:spacetime-schema
```
## Table Operations
```rust
let row = ctx.db.player().insert(Player { id: 0, owner, name, created_at });
ctx.db.player().try_insert(row)?;
let by_id = ctx.db.player().id().find(&123u64);
for player in ctx.db.player().owner().filter(&ctx.sender()) {}
for player in ctx.db.player().level().filter(&(18u32..=65u32)) {}
for player in ctx.db.player().iter() {}
let count = ctx.db.player().count();
if let Some(player) = ctx.db.player().id().find(&id) {
ctx.db.player().id().update(Player { name: new_name, ..player });
}
ctx.db.player().id().delete(&id);
```
For delete/update based on non-PK filters, collect keys first to avoid iterator invalidation.
## Indexes
```rust
#[spacetimedb::table(
accessor = score,
public,
index(accessor = by_player_level, btree(columns = [player_id, level]))
)]
pub struct Score {
pub player_id: u32,
pub level: u32,
pub points: i64,
}
for row in ctx.db.score().by_player_level().filter(&(42,)) {}
for row in ctx.db.score().by_player_level().filter(&(42, 5)) {}
```
## Event Tables
```rust
#[table(accessor = damage_event, public, event)]
pub struct DamageEvent {
pub target: Identity,
pub amount: u32,
}
#[reducer]
fn deal_damage(ctx: &ReducerContext, target: Identity, amount: u32) {
ctx.db.damage_event().insert(DamageEvent { target, amount });
}
```
Event tables must be subscribed explicitly and are excluded from `subscribe_to_all_tables()`.
Since 2.6, event tables support broader layout-altering automigrations than regular tables, including column removal, reordering, and type changes. This relaxed migration policy does not apply to persistent tables.
Event-table primary keys and constraints are enforced only within the current transaction. They do not make event rows persistent, and client SDKs expose event tables as insert-only event streams. Do not rely on `OnUpdate` / `on_update` / `onUpdate` for event tables; use a persistent table or a primary-keyed procedural view when update callbacks are required.
Official 2.4.1 through 2.7 release notes tie primary-key-backed update callbacks to procedural views, not event tables.
## Views
```rust
#[spacetimedb::view(accessor = my_players, public, primary_key = id)]
pub fn my_players(ctx: &spacetimedb::ViewContext) -> Vec<Player> {
ctx.db.player().owner().filter(&ctx.sender()).collect()
}
```
Rust and TypeScript gained primary key support for procedural views in 2.4.1. With primary keys, clients can receive update events when subscribed to such views. Avoid duplicate primary keys in view results.
## Lifecycle & Scheduled Reducers
```rust
#[spacetimedb::reducer(init)]
pub fn init(ctx: &ReducerContext) -> Result<(), String> { Ok(()) }
#[spacetimedb::reducer(client_connected)]
pub fn on_connect(ctx: &ReducerContext) -> Result<(), String> { Ok(()) }
#[spacetimedb::reducer(client_disconnected)]
pub fn on_disconnect(ctx: &ReducerContext) -> Result<(), String> { Ok(()) }
use spacetimedb::{ScheduleAt, TimeDuration};
ctx.db.game_tick_schedule().insert(GameTickSchedule {
scheduled_id: 0,
scheduled_at: ScheduleAt::Interval(std::time::Duration::from_millis(100).into()),
});
let run_at = ctx.timestamp + std::time::Duration::from_secs(60);
ctx.db.game_tick_schedule().insert(GameTickSchedule {
scheduled_id: 0,
scheduled_at: ScheduleAt::Time(run_at),
});
```
For scheduled reducers, check `ctx.sender_auth().is_internal()` when the reducer should only be system-triggered.
## Procedures
Procedures remain stable in 2.7 and no longer require the `unstable` feature.
```rust
use spacetimedb::{procedure, ProcedureContext};
#[procedure]
fn save_external_data(ctx: &mut ProcedureContext, url: String) -> Result<(), String> {
let body = ctx.http.get(url).send()?.text()?;
ctx.try_with_tx(|tx| {
tx.db.external_data().insert(ExternalData { id: 0, content: body });
Ok(())
})?;
Ok(())
}
```
| Reducers | Procedures |
|----------|------------|
| `&ReducerContext` | `&mut ProcedureContext` |
| Direct `ctx.db` access | Use `with_tx()` / `try_with_tx()` |
| No HTTP/network | Outgoing HTTP via `ctx.http` |
| Deterministic transaction path | Side-effect-capable workflow path |
In Genarrative, keep external provider protocols in `platform-*` by default unless the architecture explicitly moves that workflow into the module.
## Identity & Auth
```rust
fn require_owner(ctx: &ReducerContext, owner: &Identity) -> Result<(), String> {
if ctx.sender() != *owner {
return Err("Not authorized".to_string());
}
Ok(())
}
```
`ReducerContext::identity` is deprecated since 2.3; use the current database/module identity API when needed, and use `ctx.sender()` for caller identity.
## Commands
```bash
spacetime build
spacetime publish my_database --server http://127.0.0.1:3101 --module-path . --yes=migrate
spacetime publish my_database --server http://127.0.0.1:3101 --delete-data=on-conflict --module-path . --yes=migrate
spacetime logs my_database --server http://127.0.0.1:3101
spacetime call --server http://127.0.0.1:3101 my_database create_player '"Alice"'
spacetime sql my_database --server http://127.0.0.1:3101 "SELECT * FROM player"
npm run spacetime:generate
npm run check:spacetime-schema
```
-5
View File
@@ -130,11 +130,6 @@ VECTOR_ENGINE_BASE_URL="https://api.vectorengine.cn"
VECTOR_ENGINE_API_KEY=""
VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS="1000000"
# ElevenLabs editor sound-effect generation is server-side only.
ELEVENLABS_BASE_URL="https://api.elevenlabs.io"
ELEVENLABS_API_KEY=""
ELEVENLABS_REQUEST_TIMEOUT_MS="180000"
# 阿里云 OSS 配置。
# Rust `server-rs` 的 `api-server` 会优先从 `.env` / `.env.local` 读取这些变量,
# 用于签发浏览器 PostObject 直传票据,并保持 `/generated-*` 旧路径习惯。
-3
View File
@@ -46,7 +46,6 @@ module.exports = {
{
files: [
'apps/admin-web/src/pages/*.tsx',
'src/components/platform-entry/PlatformActiveMobileWelcomeDialog.tsx',
'src/components/platform-entry/PlatformMobileHomeWelcomeDialog.tsx',
],
rules: {
@@ -73,7 +72,6 @@ module.exports = {
'src/components/image-editor/**/*.{ts,tsx}',
'src/components/project/**/*.{ts,tsx}',
'src/components/platform-entry/PlatformActiveProfileView*.tsx',
'src/components/platform-entry/PlatformActiveMobileWelcomeDialog*.tsx',
'src/components/platform-entry/PlatformEntryActiveFlowShell*.tsx',
'src/components/platform-entry/PlatformEntryFlowShell.tsx',
'src/components/platform-entry/PlatformProfileApiKeysModal.tsx',
@@ -163,7 +161,6 @@ module.exports = {
'server-rs/target',
'server-rs/target-*',
'apps/desktop-shell/src-tauri/target',
'apps/ai-game-creator-shell/src/features/ui-editor/types/**',
'target',
'src/main.tsx',
'src/App.tsx',
+25 -27
View File
@@ -4,6 +4,7 @@ on:
push:
branches:
- master
- codex/ai-game-creator-app
pull_request:
workflow_dispatch:
@@ -58,16 +59,6 @@ jobs:
else
base_ref="$(git merge-base HEAD origin/master 2>/dev/null || git rev-parse HEAD)"
fi
resolved_base_ref="$(git rev-parse --verify "${base_ref}^{commit}" 2>/dev/null || true)"
head_ref="$(git rev-parse HEAD)"
if [[ "${resolved_base_ref}" == "${head_ref}" ]]; then
resolved_base_ref="$(git rev-parse --verify HEAD^ 2>/dev/null || true)"
fi
if [[ -z "${resolved_base_ref}" ]]; then
echo 'comparison base must resolve to a commit distinct from HEAD.' >&2
exit 1
fi
base_ref="${resolved_base_ref}"
if [[ "${GITHUB_EVENT_NAME:-}" == 'pull_request' ]] \
&& ! git merge-base --is-ancestor "${base_ref}" HEAD; then
echo 'pull request head does not contain the latest base commit; update the branch and rerun CI.' >&2
@@ -78,8 +69,23 @@ jobs:
- name: Install npm dependencies
run: bash scripts/ci-npm-ci-with-retry.sh
- name: Run repository checks
run: npm run check:repository-ci
- name: Run repository lint gates
run: npm run lint
- name: Build web applications
run: npm run build
- name: Validate content data
run: npm run check:content
- name: Check committed whitespace
shell: bash
run: |
set -euo pipefail
base_ref="${SPACETIME_SCHEMA_BASE_REF:-}"
test -n "${base_ref}"
git cat-file -e "${base_ref}^{commit}"
git diff --check "${base_ref}"...HEAD
frontend-tests:
name: Frontend tests
@@ -97,6 +103,9 @@ jobs:
- name: Install npm dependencies
run: bash scripts/ci-npm-ci-with-retry.sh
- name: Install AI game creator dependencies
run: bash scripts/ci-npm-ci-with-retry.sh --prefix apps/ai-game-creator-shell
- name: Run frontend and script tests
run: npm run test
@@ -142,16 +151,6 @@ jobs:
else
base_ref="$(git merge-base HEAD origin/master 2>/dev/null || git rev-parse HEAD)"
fi
resolved_base_ref="$(git rev-parse --verify "${base_ref}^{commit}" 2>/dev/null || true)"
head_ref="$(git rev-parse HEAD)"
if [[ "${resolved_base_ref}" == "${head_ref}" ]]; then
resolved_base_ref="$(git rev-parse --verify HEAD^ 2>/dev/null || true)"
fi
if [[ -z "${resolved_base_ref}" ]]; then
echo 'comparison base must resolve to a commit distinct from HEAD.' >&2
exit 1
fi
base_ref="${resolved_base_ref}"
if [[ "${GITHUB_EVENT_NAME:-}" == 'pull_request' ]] \
&& ! git merge-base --is-ancestor "${base_ref}" HEAD; then
echo 'pull request head does not contain the latest base commit; update the branch and rerun CI.' >&2
@@ -183,10 +182,7 @@ jobs:
run: npm run check:server-rs-ddd
- name: Run server-rs workspace tests
run: cargo test --locked --workspace --exclude spacetime-module --no-fail-fast --manifest-path server-rs/Cargo.toml
- name: Run SpacetimeDB module unit tests
run: cargo test --locked -p spacetime-module --no-fail-fast --manifest-path server-rs/Cargo.toml
run: cargo test --locked --workspace --no-fail-fast --manifest-path server-rs/Cargo.toml
- name: Check api-server targets
run: cargo check --locked -p api-server --all-targets --manifest-path server-rs/Cargo.toml
@@ -210,12 +206,14 @@ jobs:
- name: Install npm dependencies
run: bash scripts/ci-npm-ci-with-retry.sh
- name: Install AI game creator dependencies
run: bash scripts/ci-npm-ci-with-retry.sh --prefix apps/ai-game-creator-shell
- name: Prepare native Rust dependencies
shell: bash
run: |
set -euo pipefail
for manifest_path in \
server-rs/Cargo.toml \
apps/desktop-shell/src-tauri/Cargo.toml \
apps/ai-game-creator-shell/src-tauri/Cargo.toml; do
for attempt in $(seq 1 5); do
+1 -11
View File
@@ -34,19 +34,11 @@ temp*build*/
/apps/ai-game-creator-shell/src-tauri/target/
/apps/ai-game-creator-shell/src-tauri/gen/
/apps/ai-game-creator-shell/src-tauri/logs/
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/codex.exe
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/manifest.json
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/bin/
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/codex-path/
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/codex-resources/
/apps/ai-game-creator-shell/src-tauri/resources/codex/win-x64/codex-package.json
/apps/ai-game-creator-shell/logs/
/apps/ai-game-creator-shell/.llm-drafts/
/apps/ai-game-creator-shell/game-creator.config.local.json
/apps/mobile-shell/.expo/
/apps/mobile-shell/.expo-export-smoke/
/apps/preview-deployer-web/dist/
/apps/preview-deployer-web/node_modules/
/server-rs/.spacetimedb/
/server-rs/.data/
/public/generated-animations
@@ -54,9 +46,7 @@ temp*build*/
/public/generated-characters
/.codex-temp
/.app/
/.jenkins-source-commit
/.jenkins-spacetime-schema-base
target/
/target/
/logs
/.claude/settings.local.json
/.codegraph/
+66
View File
@@ -0,0 +1,66 @@
# Genarrative Hermes 工具目录
本目录只保留 Hermes 专用的仓库级工具资源,例如 Hermes skills、plugins 和启用说明。项目知识本体、长期记忆、计划和 TODO 不再放在 `.hermes/`,统一迁移到 `docs/project-memory/`
## 使用原则
- `.hermes/` 中只保存 Hermes 工具运行或加载所需内容。
- 项目长期知识、架构约定、排障经验、协作规则、计划和 TODO 统一放在 `docs/project-memory/`
- 不提交个人配置、API Key、会话转录、模型密钥、本地路径密钥等敏感内容。
- 个人 Hermes 的 `~/.hermes/config.yaml``~/.hermes/.env``~/.hermes/sessions/` 不应复制到本仓库。
- 后续新增的 Markdown 文档文件名必须以分类标签开头,格式为 `【标签名】中文标题-日期.md`,便于团队跨目录检索。
-`.hermes/` 中的工具说明与代码或 `docs/` 冲突,以当前代码和最新 `docs/` 为准。
## 目录结构
```text
.hermes/
├─ README.md # Hermes 工具目录说明
├─ skills/ # 仓库级 Hermes skills
└─ plugins/ # 仓库级 Hermes plugins(需显式启用项目 plugin
```
## 仓库级 Plugins
本仓库可共享的 Hermes plugin 放在 `.hermes/plugins/<plugin-name>/`。当前已包含:
- `.hermes/plugins/game-studio/`:浏览器游戏设计、原型、2D/3D 技术栈、素材管线与 playtest 相关工作流。
Hermes 的项目级 plugin 默认不会自动加载。团队成员拉取仓库后,如需使用本仓库内 plugin,请在仓库根目录启动 Hermes 前设置:
```bash
export HERMES_ENABLE_PROJECT_PLUGINS=1
```
然后确认当前 Hermes 配置的 `plugins.enabled` 中包含 `game-studio`。如果成员本机尚未启用过该 plugin,当前 Hermes 的 `hermes plugins enable` 只识别用户级或内置 plugin,可能不会识别项目级 plugin;可用以下命令写入个人配置:
```bash
python - <<'PY'
from hermes_cli.config import load_config, save_config
config = load_config()
plugins = config.setdefault('plugins', {})
enabled = set(plugins.get('enabled') or [])
disabled = set(plugins.get('disabled') or [])
enabled.add('game-studio')
disabled.discard('game-studio')
plugins['enabled'] = sorted(enabled)
plugins['disabled'] = sorted(disabled)
save_config(config)
PY
```
启用后重新进入一个新 Hermes 会话。`hermes plugins list` 当前主要展示内置和用户级 plugin,未必列出项目级 plugin;如需验证项目级扫描,可在仓库根目录运行:
```bash
HERMES_ENABLE_PROJECT_PLUGINS=1 HERMES_PLUGINS_DEBUG=1 hermes chat -q "请读取 game-studio:game-studio skill 并概括它的用途"
```
该 plugin 注册的是带命名空间的 plugin skills,可用类似 `game-studio:phaser-2d-game` 的名称显式加载。
## 推荐给 Hermes 的启动提示
在本仓库中开始复杂任务时,可以先对 Hermes 说:
```text
请先读取 AGENTS.md 以及 docs/project-memory/shared-memory/ 下与本任务相关的团队共享记忆,再开始分析。若任务完成后产生稳定项目知识,请更新 docs/project-memory/shared-memory/ 对应文件。
```
@@ -1,8 +1,8 @@
"""Metadata bridge for the bundled Codex Game Studio project plugin.
"""Hermes wrapper for the OpenAI Codex Game Studio plugin.
The manifest and bundled ``SKILL.md`` files are the source of truth. This
module only exposes their descriptions to a compatible plugin host and keeps
the project resource bundle free of runtime-specific behavior.
This plugin was imported from a Codex curated plugin cache. It exposes the
plugin's bundled SKILL.md files as Hermes plugin skills using qualified names
like `game-studio:phaser-2d-game`.
"""
from __future__ import annotations

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

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