合并 master 并保留 GPT Image 2.5 与外置提示词行为
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m1s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 3m54s
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 8m1s
Project CI / Native shell tests (pull_request) Successful in 9m25s
Project CI / Backend tests (pull_request) Successful in 10m37s
Project CI / Repository checks (pull_request) Successful in 7m52s
Project CI / Frontend tests (pull_request) Failing after 10m40s
Project CI / AI game creator shell web tests (pull_request) Failing after 5m25s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m1s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 3m54s
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 8m1s
Project CI / Native shell tests (pull_request) Successful in 9m25s
Project CI / Backend tests (pull_request) Successful in 10m37s
Project CI / Repository checks (pull_request) Successful in 7m52s
Project CI / Frontend tests (pull_request) Failing after 10m40s
Project CI / AI game creator shell web tests (pull_request) Failing after 5m25s
保留编辑器图片工具使用 gpt-image-2.5 业务模型名 保留工具参数说明从 prompts 外置文件读取 同步外置模型与尺寸文案并完成冲突验证
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
|
||||
## 1. 一句话
|
||||
|
||||
功能画布 = 资源栏目页;栏目页左下角渲染一条由栏目 `category` 决定的工具栏,图片类入口走本地 `start_local_project_asset_generation`(提交即返回、后台生成),音频入口复用既有无源生成链路,上传复用 `upload_local_asset`;生成 / 上传成功后一律走既有 manifest 刷新与资源定位。
|
||||
功能画布 = 资源栏目页;栏目页左下角渲染一条由栏目 `category` 决定的工具栏,图片类与音频入口都走本地 `start_local_project_asset_generation`(提交即返回、后台生成,见 2026-09-20 音频并入后台任务账本),上传复用 `upload_local_asset`;生成 / 上传成功后一律走既有 manifest 刷新与资源定位。
|
||||
|
||||
## 2. 入口矩阵(事实源)
|
||||
|
||||
@@ -40,13 +40,13 @@
|
||||
| 生成规范 → 自定义规范 | 同上 | `kind: 'icon-spec'`,固定 `1:1 · 1K` |
|
||||
| 生成图标素材 | 同上 | `kind: 'icon-spritesheet'`,默认 `1:1 · 1K`;前置:已登记 `assets/art-spec.png` |
|
||||
| 生成 UI 设计图 | 同上 | `kind: 'ui-design'`,默认 `16:9 · 1K`;前置同上 |
|
||||
| 生成背景音乐 | `derive_local_project_resource` | `editKind: 'background-music'`、`generationMode: 'create'`、`sourceMediaType: 'audio/mpeg'` |
|
||||
| 生成音效 | `derive_local_project_resource` | `editKind: 'sound-effect'`、其余同上 |
|
||||
| 生成背景音乐 | `start_local_project_asset_generation` | `kind: 'background-music'`、`idempotencyKey`;任务 id 即该次生成的 operation id |
|
||||
| 生成音效 | `start_local_project_asset_generation` | `kind: 'sound-effect'`、其余同上 |
|
||||
| 上传 | `upload_local_asset` | `{ projectPath, fileName, mediaType, bytes }` |
|
||||
|
||||
`start_local_project_asset_generation` 的完整参数是 `{ projectPath, projectId, taskId, kind, prompt, aspectRatio, imageSize, assetName, outputPath }`(Rust `src-tauri/src/asset_generation_tasks.rs`),**提交即返回**一条任务记录(`taskId / status / phaseDetail / assetId / error` 等);生成在 `tauri::async_runtime::spawn` 出来的后台任务里跑,账本落在项目内 `.agent/runtime/asset-generation-tasks/tasks.json`,进度由 `list_local_project_asset_generations` 读回。截图面板不再「等生成结束」,所以**点「生成」即同步关闭面板**(不等 IPC),面板内不出现阶段文案;只有「点击瞬间就失败」才带草稿重开(关闭 ≠ 取消)。
|
||||
`start_local_project_asset_generation` 的完整参数是 `{ projectPath, projectId, taskId, kind, prompt, aspectRatio, imageSize, assetName, outputPath, idempotencyKey }`(Rust `src-tauri/src/asset_generation_tasks.rs`);图片类入口沿用 `{ projectPath, projectId, taskId, kind, prompt, aspectRatio, imageSize, assetName, outputPath }` 逐字不变,音频入口只带 `{ projectPath, projectId, taskId, kind, prompt, assetName, idempotencyKey }`(`idempotencyKey` = 该次生成的幂等键,任务 id 即 operation id;原生命令内部仍复用既有音频无源生成链路,不新增平台路由与请求体口径),**提交即返回**一条任务记录(`taskId / status / phaseDetail / assetId / error` 等);生成在 `tauri::async_runtime::spawn` 出来的后台任务里跑,账本落在项目内 `.agent/runtime/asset-generation-tasks/tasks.json`,进度由 `list_local_project_asset_generations` 读回。截图面板不再「等生成结束」,所以**点「生成」即同步关闭面板**(不等 IPC),面板内不出现阶段文案;只有「点击瞬间就失败」才带草稿重开(关闭 ≠ 取消)。
|
||||
|
||||
入参 `kind` 一律是共享 `GameCreationAppAssetKind` 的 canonical 成员(`image` / `character` / `icon-spec` / `icon-spritesheet` / `ui-design` / `publication-material`);`spec` / `art-spritesheet` / `ui-prototype` 这类平台请求词汇只由 Rust 侧派生到 `source.generationKind`,不再出现在 IPC 载荷或 manifest kind 里。
|
||||
入参 `kind` 一律是共享 `GameCreationAppAssetKind` 的 canonical 成员(图片类 `image` / `character` / `icon-spec` / `icon-spritesheet` / `ui-design` / `publication-material`,音频 `sound-effect` / `background-music`);`spec` / `art-spritesheet` / `ui-prototype` 这类平台请求词汇只由 Rust 侧派生到 `source.generationKind`,不再出现在 IPC 载荷或 manifest kind 里。
|
||||
|
||||
## 4a. 本地排队与「生成任务」侧栏
|
||||
|
||||
@@ -74,6 +74,8 @@
|
||||
|
||||
- 音频画布的「生成背景音乐 / 生成音效」由工具栏承载:`ResourceCanvasGenerationPanelView` 新增 `kinds` prop,工具栏各自只放行自己那一种,单类型入口不再渲染类型选择器,面板标题与提交文案跟该类走。
|
||||
- 既有「生成素材」浮层入口传 `kinds={['video']}`,只保留视频;视频能力不删,只是不出现在工具栏里。
|
||||
- 音频入口与图片类入口共用同一份后台任务账本(2026-09-20):提交即返回任务记录、面板同步关闭、状态与阶段文案只由后端账本提供、任务出现在「生成任务」侧栏并在重开项目后恢复。音频任务的**请求身份**(operation id 与幂等键)随任务一起走:任务 id 即 operation id,重试复用同一对身份,不会变成第二次付费生成;音频面板与图片类面板一致,只有「点击瞬间就失败」(未受理)才带原草稿与原请求身份重开。
|
||||
- 音频生成在客户端后台跑时仍复用既有音频无源生成链路(`generationMode: 'create'`、`editKind` = `sound-effect` / `background-music`、源快照媒体类型由 `editKind` 推为 `audio/mpeg`——Create 模式不读 `sourceMediaType` 入参、平台路由与请求体口径不变);音频生成没有可播报的中间进度,因此阶段文案仍是账本的「排队中。」/「正在生成。」/「生成已完成。」/失败原因四档,不新增假阶段。
|
||||
|
||||
## 8. 验证
|
||||
|
||||
@@ -93,6 +95,7 @@ cargo test -p genarrative-ai-game-creator-shell asset_generation_task
|
||||
|
||||
- 模型层:四个栏目的工具集与顺序、未命中栏目返回空、「所有资源」与 `null` 返回空、二级菜单分流、比例 / 尺寸白名单收窄、前置判据、不可用原因、`outputPath` 策略。
|
||||
- 组件层:工具栏渲染与二级菜单开合、不可用入口可点击 + 原因可关闭、上传回调。
|
||||
- 生成任务层:`resourceCanvasAssetGenerationTaskModel`(状态机 / 在途判据 / 恢复 / 耗时文案)、`resourceCanvasAssetGenerationQueue`(第二条不发提交 IPC、前一条终态后自动补发、失败与账本丢失都能收口)、`ResourceCanvasAssetGenerationTasksPanelView`(非模态、阶段文案来自后端记录、按 `assetId` 定位)、两块生成浮层在提交期间可关闭、「后台运行并关闭」文案。
|
||||
- 生成任务层:`resourceCanvasAssetGenerationTaskModel`(状态机 / 在途判据 / 恢复 / 耗时文案、音频任务载荷分流)、`resourceCanvasAssetGenerationQueue`(第二条不发提交 IPC、前一条终态后自动补发、失败与账本丢失都能收口、音频只发任务身份 + 幂等键)、`ResourceCanvasAssetGenerationTasksPanelView`(非模态、阶段文案来自后端记录、按 `assetId` 定位)、两块生成浮层点「生成」即同步关闭(面板 DOM 里没有阶段文案与「后台运行并关闭」这类在途按钮)。
|
||||
- Rust 层:`asset_generation_tasks` 的账本落项目内、排队阶段文案由后端拥有、进程重启把无人推进的记录收口为中断失败、账本上限与 task id 边界。
|
||||
- AppSurface 层:进入栏目出现工具栏 / 回总览与展开态消失、每个图片类入口的 `start_local_project_asset_generation` 载荷、前置缺失可点击说明且零请求、音频入口走 `derive_local_project_resource`、上传走 `upload_local_asset` + 配对读清单、生成面板关闭后任务仍在「生成任务」面板且第二条按本地排队补发、工具栏与右下 Dock 的几何契约。
|
||||
- 音频后台化:音频 kind 的提交返回任务记录且账本 `kind` 为音频、提示词上限与幂等键在提交时收口(未知 kind / 非法身份在提交即拒绝)、第二条提交在本地排队期间不发 IPC、未受理失败重开面板并复用同一 operation 与幂等键、受理后失败的收口与落卡复用图片类同一链路。
|
||||
- AppSurface 层:进入栏目出现工具栏 / 回总览与展开态消失、每个图片类入口的 `start_local_project_asset_generation` 载荷、前置缺失可点击说明且零请求、音频入口走同一条命令的音频载荷(`idempotencyKey` + 任务 id 即 operation id)、上传走 `upload_local_asset` + 配对读清单、生成面板关闭后任务仍在「生成任务」面板且第二条按本地排队补发、工具栏与右下 Dock 的几何契约。
|
||||
|
||||
@@ -71,7 +71,7 @@ plugins/agc-cocos-editor/
|
||||
|
||||
宿主按 `AGC_PLUGIN_WORKSPACE`、随包 `<resource_dir>/plugins`、开发构建仓库 `plugins/` 的顺序解析工作区;插件包内的 `native/payload` 由构建脚本随包映射,生成的 DLL 不入库。插件协议、权限和面板挂载全部复用通用宿主,Cocos 专属逻辑只存在于本插件包:进程名与 `--project` 解析、Creator 版本校验、named pipe 协议和 Windows 注入。
|
||||
|
||||
该插件是**内置插件**:随客户端分发、不能卸载,只能通过 `set_agc_plugin_enabled` 控制是否可用。除开关和 feature 外,还必须满足“当前受控项目已识别为 Cocos Creator 项目”这一门禁;没有当前项目或项目类型不是 Cocos 时,插件不出现在插件列表、面板、Agent 工具目录或 MCP tools/list 中,启动、面板读取、RPC 和编辑器执行也会失败关闭。项目切换离开 Cocos 后,已运行实例立即停止。Cocos 编辑器操作统一优先通过该内置插件的 `cocos.editor.execute` / `cocos.editor.operation`(DirectProject 对应 `agc_cocos_execute`);不得改走项目目录 `extensions/`、`package.json` 插件或第三方 MCP。开关状态保存在 AppData `extensions/builtin-plugins.json`,隔离 MCP 每次 tools/list 都向绑定宿主询问当前状态与项目门禁。
|
||||
该插件是**内置插件**:随客户端分发、不能卸载,只能通过 `set_agc_plugin_enabled` 控制是否可用。插件列表、启动、面板读取、插件 RPC、Agent 工具目录和 MCP tools/list 不按当前工程类型过滤;无当前项目或非 Cocos 项目仍沿用相同的开关、原生适配器、平台和 feature 规则。项目切换不因工程类型不同而停止插件,仍更新受控项目上下文并失效旧连接。实际编辑器操作必须取得当前受控项目对应的真实 Creator 目标并通过原有目录、PID、版本与握手校验;无项目或非引擎目录不能仅凭工具可见就通过执行校验。Cocos 编辑器操作统一优先通过该内置插件的 `cocos.editor.execute` / `cocos.editor.operation`(DirectProject 对应 `agc_cocos_execute`);不得改走项目目录 `extensions/`、`package.json` 插件或第三方 MCP。开关状态保存在 AppData `extensions/builtin-plugins.json`,隔离 MCP 每次 tools/list 都向绑定宿主询问当前可用状态,不自行按工程类型过滤。
|
||||
|
||||
## AGC 项目打开入口
|
||||
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
# AGC Godot 编辑器插件接入
|
||||
|
||||
> 文档状态:`current`
|
||||
> 规范关系:承接 AGC 通用插件宿主与编辑器适配主规范
|
||||
|
||||
更新时间:`2026-09-20`
|
||||
|
||||
## 目标与边界
|
||||
|
||||
常用操作指导随客户端审核 Skill pack 提供,入口为 `agc-godot-editor`。DirectProject 按需读取 [Skill 入口](../../apps/ai-game-creator-shell/src-tauri/resources/agc-skills/agc-godot-editor/SKILL.md) 和其常用操作参考;Runtime 的 `godot.editor.execute` 工具说明包含同一参考正文。指导覆盖场景/节点、owner、PackedScene、资源、UI、保存与撤销,不新增专用操作工具,不改变执行授权。
|
||||
|
||||
原文示例在 Godot 4.7.2 标准版 headless 工程验证了节点回读、局部撤销、PackedScene 存读、居中 UI 结构、无缩略图保存重开及只读旧文件写失败时保留内存修改。`save_scene_as` 不返回错误码,指南在重开前核验磁盘包含本次预期变更,不能以旧文件可加载作为保存成功证据。复验时设置 `AGC_GODOT_TEST_EXECUTABLE`,运行 `node --test plugins/agc-godot-editor/native/gdextension/tests/guide-examples.test.mjs`。正式 Ctrl+Z 历史、运行中停止、GUI 缩略图保存及 UI 视觉仍未由该测试验收。
|
||||
|
||||
独立图形环境补验已确认该示例在 800×600、480×800、1280×720 三种实际渲染尺寸下中文和按钮正常显示、容器居中且无裁切;此结果只覆盖示例布局,不代表按钮已接入游戏逻辑或其他 UI 已验收。
|
||||
|
||||
将 Godot 编辑器操控接入现有 AGC PluginHost、EditorAdapter、Runner、内置插件开关、权限审计和 Agent 工具链。Windows x64 的 Godot 4.7 及以上标准编辑器是首个实现目标,实机验收使用 4.7.2;其他平台和 .NET 编辑器不得从该结果推断支持。
|
||||
|
||||
初版工程路径支持 Windows 本地盘符目录;UNC/网络共享路径在准备描述文件前明确拒绝。链接/reparse point 继续按同一文件边界失败关闭。
|
||||
|
||||
DLL 原件随 AGC 安装包放在插件资源目录中;Godot Windows 加载器会在被加载文件旁生成 `~DLL`,因此宿主在 AGC 私有配置目录的运行缓存中按编辑器实例和构建身份准备临时加载副本。AGC 向项目新增一个可扫描的受管 `.gdextension` 描述文件,通过绝对路径引用该实例的加载副本;Godot 可自动生成其同名 `.uid` 伴生文件。DLL 不复制进工程。重新聚焦 Godot 后,由官方文件扫描完成首次加载;不需要用户打开或运行脚本,不创建 EditorPlugin addon,不修改 project.godot 或业务场景文件。编译工具链只属于开发与打包环境,不要求终端用户安装编译器。
|
||||
|
||||
本次包含连接、状态、GDScript 执行和真实回执、断开及资源清理,并验证通过代码读取和修改独立测试场景。专用截图/输入/场景工具目录、云服务、额外 MCP 服务、公开后端 API 和发布上传不在本次范围;通用执行可以调用 EditorInterface,不能把文件生成冒充编辑器内执行。
|
||||
|
||||
## 入口与归属
|
||||
|
||||
- 插件 id 为 `agc-godot-editor`,适配器为 `godot-editor`;命令 `godot.editor.execute`、连接能力 `godot.editor.connection`,DirectProject 工具为 `agc_godot_execute`。复用已有扩展列表和启用开关,不建立平行插件管理页面。
|
||||
- 项目发现沿用现有 Godot 工作区合同:工作区根保持用户选定目录;实际 Godot 根由普通 project.godot 在根或唯一一层子目录中确定。准备描述文件和读取 Godot 缓存只作用于实际 Godot 根,通用文件工具/Runtime 的工作区根不改变。
|
||||
- 平台、内置开关、项目及目标身份必须在执行入口重新检查。插件只能处理宿主传入的当前受控项目,模型不能覆盖项目路径、DLL 路径、端口、令牌或目标实例。
|
||||
- Godot 的插件列表、启动和 Agent 工具目录继续按当前 Godot 项目过滤;Cocos/Unity 沿用各自不按工程类型过滤的合同。前端统一消费宿主列表自动启动三种编辑器插件,不在界面重复推断项目类型。Godot 的项目切换仍撤销旧插件上下文并停止旧实例。
|
||||
- 插件启动先完成项目事件订阅并接收当前受控项目快照,再注册命令和连接能力;命令可见时必须已经具备执行上下文。订阅期间收到的新项目事件优先于迟到的初始快照。
|
||||
- 只连接已打开且唯一匹配真实工程路径的 Godot Editor;校验 PID、进程启动身份、Godot 版本、握手中的工程路径与会话代次。多个候选、非编辑器、路径不符或已退出的进程均拒绝,不启动或关闭用户编辑器。
|
||||
- GUI、DirectProject 和 Agent Runtime 的原生操作统一由长寿命 Runner 持有。项目切换使连接失效,迟到回执不能改变新项目状态。
|
||||
|
||||
## 分发与描述文件
|
||||
|
||||
- 原生引导使用官方 `godot-cpp` 的 C++ 类型和初始化接口,不自行声明 Variant 存储或直接装配 ABI 函数指针。绑定源码固定到 `godot-4.5-stable` 的 `e83fd0904c13356ed1d4c3d09f8bb9132bdc6b77`,以该版本的稳定 API 构建并在 Godot 4.7.2 验证;产品最低版本仍为 4.7,因为嵌入脚本使用该版本能力。Windows x64 构建使用 Visual Studio C++、CMake 和 Python,静态链接绑定及 C++ runtime,用户无需这些构建工具。
|
||||
- Jenkins 的 `Genarrative-Agc-Windows-Build` 用 `AGC_WINDOWS_PATH` 整体替换阶段 PATH,因此 Godot 原生扩展所需的 CMake 与 Python 3 必须显式列入该白名单(当前为 Build Tools 自带的 `C:\BuildTools\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin` 与 `C:\Python312`);Toolchain preflight 校验 CMake ≥3.25、Python 3 和 Visual Studio 17 2022 生成器,缺失即失败关闭。
|
||||
- 构建仅从已固定的官方归档获取绑定源码,并核对 SHA256;下载和生成内容只进入 `.build/`。CMake 构建包含引导源码、嵌入脚本、绑定版本/归档摘要和构建配置的身份指纹。许可证与来源继续随 DLL 分发,安装包不包含 SDK、源码缓存、生成绑定或构建工具。
|
||||
- C++ 状态仅在扩展有效期间持有 GDScript 引用和桥节点身份;终止回调先停用仍存活的桥,再释放绑定对象,不能让静态 Godot 对象析构晚于绑定退出。延迟 bootstrap、原生卸载、Node 已退出及同 PID 重连均须实测。受管文件、会话身份、协议、执行回执与不确定阻断沿用现有合同。
|
||||
- EDITOR 阶段晚加载不会取得 CORE 阶段终止回调;引导终止时须通过官方绑定接口解除 Node/GDScript 的实例包装回调,并完成单例包装清理。只移除 C++ 包装,不同步销毁仍在 GDScript 调用栈或等待 `queue_free` 的引擎对象;先用 Variant 保活脚本,再解除 Ref 和绑定,避免卸载 DLL 后跳到失效回调。
|
||||
|
||||
- 安装资源布局为 `plugins/agc-godot-editor/native/gdextension/bin/win-x64/agc_godot_editor.dll`,邻接元数据记录协议、构建身份和 DLL SHA256。开发模式允许宿主提供仓库插件目录中的同结构产物;RPC 不接受自定义 DLL 候选。
|
||||
- 缓存根仅由宿主提供,为其私有配置目录下的 `godot-editor-runtime`;按 `PID + startedFileTime + buildId` 隔离,所有路径分量受控且拒绝链接/reparse point。复制前验证安装原件及元数据,缓存已有文件必须匹配来源、归属及 SHA,不能加载被替换的同名文件。受管描述同时保留原件与加载副本身份,重启恢复不得把工程给出的任意 DLL 路径当作受信任来源。
|
||||
- 实际模块核验同时覆盖加载副本及 Godot 在同目录生成的精确 `~agc_godot_editor.dll`,要求规范化路径和 DLL 字节身份一致。确认原生卸载后,只清理本实例、本构建、内容未变的缓存文件;不能跨编辑器删除或复用影子副本。安装位置更新和实例 PID 重用都必须重新验证。
|
||||
- 原生扩展只加载自身受信任包内的实现;GDScript 桥源码编译进 DLL,目标工程不能替换引导脚本。保留 Godot 官方 ABI 来源及 MIT 许可;生成的 DLL、缓存和机器路径不提交。
|
||||
- 描述文件固定为实际 Godot 根下的 `agc-editor-bridge.gdextension`,带 AGC 所有权标记和构建身份。首次使用可创建,内容一致时不重写;安装路径或构建身份变化时只更新本插件拥有的文件。已有同名非受管文件、链接/reparse point 或未知内容必须拒绝覆盖。
|
||||
- `.agent`、点号目录和 `.gdignore` 路径不会被 Godot 自动扫描,因此描述文件不能放在那里。会话发现文件仅放在 `.godot/agc/` 缓存内,不进入 manifest、对话或日志;校验缓存各级目录没有链接跳转。
|
||||
- 描述文件及引擎自动生成的 `.uid` 按同一归属管理:创建描述文件前已有孤立同名 UID 时拒绝接管;扫描生成后在 `.godot/agc/` 记录描述内容及 UID 内容指纹。重连、升级和清理核对此记录,仅删除原内容未变的本插件伴生文件;记录丢失、用户改动或未知同名文件时保留并报告,不把“格式合法”当作删除授权。
|
||||
- connect/execute 可以准备受管描述文件,并尝试把经过验证的目标编辑器窗口置前触发扫描;若系统不允许聚焦或握手未就绪,返回明确的未派发错误,提示重新聚焦后连接。连接重试不重放业务代码。
|
||||
- 升级、断开和禁用时先通过有效旧连接停用内存桥/卸载原生扩展,再清理与本会话匹配的受管描述文件和会话缓存。用户改动过的文件不删除;无可信握手时不能把删除文件当作已卸载。编辑器已退出时允许清理已确认归属的本地痕迹。
|
||||
- 安装目录可能只读;插件不得向 DLL 所在目录写令牌、状态或日志。
|
||||
- 安装位置/构建身份升级必须先确认旧扩展已卸载,再原子更新受管描述文件。旧 DLL 仍被目标进程加载、shutdown 结果不明或旧会话无法核实时,保留痕迹并返回可诊断错误,不用替换引用冒充完成升级。
|
||||
- 正式描述文件禁用自动 DLL 热重载;首次聚焦发现加载不受此设置影响。版本/路径更新走上述受控卸载和新加载,防止引擎自动热重载越过正在执行或待核对的任务。
|
||||
|
||||
## 执行协议与结果
|
||||
|
||||
引擎端协议固定为 `agc.godot.editor.v1`。本机回环 TCP 的 JSONL 每个请求包含 `protocol`、正整数 `id`、`generation`、`token`、`method` 和 `params`;方法为 `status`、`execute`、`shutdown`。会话缓存字段为 `protocol`、`buildId`、`pid`、`startedFileTime`(字符串)、`generation`、`projectPath`、`version`、`port`、`token`。文件名为 `editor-bridge-<pid>.json`,单文件最多 64 KiB。每次加载产生随机代次和令牌,端口只监听 127.0.0.1。
|
||||
|
||||
每条响应回显 `protocol/id/generation/pid/projectPath/buildId`,并包含 `result`;执行的 result 沿用 AGC 结构:`ok`、`status`、`dispatched`、`retryAllowed:false`、成功 `result` 或失败 `error:{code,message}`,可附有界日志。状态为 `completed`、`failed`、`needs-reconciliation`。连接状态沿用 `EditorConnectionInfo`,可增加代次、就绪与版本诊断字段。
|
||||
|
||||
`execute.params` 固定为 `{code:string,timeoutMs:integer}`;`timeoutMs` 为 1..60000 的剩余预算。`status.params` 和 `shutdown.params` 均为空对象。status 的 result 为 `{connected:true,pid,projectPath,version,generation,buildId,executing:boolean}`。shutdown 在没有在途执行时返回 `{accepted:true,status:"shutting-down"}`,仅表示停机请求已受理;桥在发送该回执后移除自己的会话缓存、关闭监听并卸载扩展。宿主必须继续校验同一目标进程的原生模块已卸载、原代次会话消失,才允许删除/替换描述文件或投影为断开完成。拒绝停机返回 `{accepted:false,error:{code,message}}`,不能把 accepted 当作卸载完成回执。
|
||||
|
||||
- GDScript 是可含 return/await 的函数体,非空、不含 NUL,最多 128 KiB;请求和回执最多 2 MiB,日志有界。执行运行于编辑器主线程,临时桥的 owner=null,不加入用户场景。
|
||||
- 只有真实执行完成且没有捕获到脚本运行错误才返回 completed。编译失败和确定运行失败返回可修复诊断,不以 nil 返回值伪装成功;空值返回本身仍是合法结果。执行日志不暴露令牌或宿主凭据。
|
||||
- 使用同一总期限覆盖发现、准备、连接、发送和读取;并发写执行立即拒绝,不积压截止后可能被派发的代码。原生服务不自动重发 execute。
|
||||
- 发送前的参数/路径/权限/平台/未连接错误为 `failed, dispatched:false`;发送后的超时、断线、损坏或身份不符回执为 `needs-reconciliation, dispatched:true, retryAllowed:false`。
|
||||
- 主线程无限循环不能承诺硬中止。async 未返回或运行状态不明时必须保留不确定阻断,不能因重连、插件启停、切换工程或 Runner 自动重启消除。
|
||||
- await 全程保持单执行占用;未完成或结果不确定期间,shutdown/禁用/切换只能禁止新增执行,不能卸载正在使用的桥或清除 fence。待状态已知且无在途执行后再完成资源清理;不确定时返回明确待核对状态。
|
||||
- 复用现有执行回执确认机制:Runner 派发前持久化请求身份;调用者确认完整匹配的终态回执后才能清理 pending。丢失最后一跳回执保持阻断,只有核对后退出全部 AGC/Runner 并重新打开才能恢复。
|
||||
- fence 的持久范围是同一完整宿主会话:自动重启 Runner、新窗口、JS 插件重载均不能清除。只有用户已核对编辑器状态、全部旧 AGC/Runner 退出,且新 GUI 同时独占现有 GUI 参与锁与 Runner 实例锁时,才能沿用现有恢复入口开始新的宿主会话;该规则与 Unity 一致,不建立另一套自动 reconcile。
|
||||
|
||||
## 契约与兼容
|
||||
|
||||
不修改服务端 API、DTO 结构、SpacetimeDB schema 或游戏持久业务数据。已有项目命令目录增加 `godot.editor.execute`,默认权限为 confirm,Rust 与 TypeScript 镜像保持一致;实际执行继续服从当前运行档及项目权限策略。通用 EditorAdapter 文档允许编辑器适配器按自身已授权合同维护受管引导文件;Cocos/Unity 的不写工程连接行为保持原约定。内置开关和审计继续使用已有存储。现有项目没有描述文件时按首次连接创建,不引入历史兼容路径。
|
||||
|
||||
## 验收
|
||||
|
||||
| 条款 | 必须取得的证据 |
|
||||
| --- | --- |
|
||||
| 包与来源 | 原生 DLL 构建、官方 ABI/许可、源码内无机器路径;staging 校验 DLL 及元数据只进入 Windows x64 资源 |
|
||||
| 受管文件 | 首次生成、幂等、安装路径更新、非受管文件冲突、路径穿越/链接拒绝、正常卸载及异常保留 |
|
||||
| 目标身份 | 根/一层 Godot 项目,PID/启动身份/工程/代次/构建身份校验;错误目标拒绝 |
|
||||
| 宿主接入 | manifest、启停、开关、权限、Runner RPC、DirectProject/Runtime 工具和项目切换定向测试 |
|
||||
| 执行 | 42、场景读取与独立场景修改/撤销、nil、编译错、运行错、async、有界日志和拒绝并发 |
|
||||
| 不确定结果 | 发送前后失败分类、超时/断线/损坏回执、ACK 归属、插件及 Runner 重启不解除阻断 |
|
||||
| 实机 | 已打开的独立 Godot 4.7.2 工程中无需脚本 UI 操作的首次加载、执行、卸载、重新连接;DLL 保持安装资源布局且原有工程文件哈希不变 |
|
||||
| 仓库门禁 | 相关 Rust/JS/前端定向测试、类型检查、文档索引、编码和 git diff --check;真实编辑器、打包资源与安装包 UI 的结果分别说明 |
|
||||
|
||||
## 已确定的产品选择
|
||||
|
||||
用户明确选择 DLL 原件留在 AGC 安装目录,并确认每个编辑器的临时加载副本放 AGC 缓存,工程内不复制 DLL。正式实现已按下列证据重新验收,前置 PoC 不作为交付依据。
|
||||
|
||||
## 本地验收结果(2026-09-20)
|
||||
|
||||
Windows x64、Godot 4.7.2 标准编辑器的本地实现验收通过。证据保存在 gitignored 的 `.app/diagnostics/`,不随源码提交;复验入口保留在插件源码和现有测试中。下列测试集合存在交叉,不相加为总用例数。
|
||||
|
||||
| 验收面 | 已取得的证据 |
|
||||
| --- | --- |
|
||||
| 原生服务与文件边界 | `godot-cache-tests-final.log`:30 项通过;覆盖受管文件及 UID、目标身份、缓存来源/哈希、安装更新、实例隔离、链接拒绝、异常保留和不确定状态 |
|
||||
| 真实引擎原生执行 | `godot-native-final-tests.log`:12 项通过、0 跳过;真实 headless Godot 验证同步/async、编译/运行错、超时占用、有界输出、代次拒绝及卸载重连 |
|
||||
| 插件与宿主 | `godot-plugin-js-final.log`:Cocos/Unity/Godot JS 共 31 项通过;`godot-shell-final-tests.log`:Godot 过滤 29 项通过;`godot-host-final-tests.log`:PluginHost 16 项通过,含切项目撤销旧上下文、派发前取消及派发后回执丢失 |
|
||||
| 共享权限与前端 | `godot-web-final-tests.log`:28 项通过;Rust 命令权限契约通过,AGC typecheck 通过;Godot 命令继续使用默认 confirm |
|
||||
| 现有宿主回归 | EditorAdapter、Unity、Runner 重启 fence 和缺失 endpoint 清理的定向测试通过;构建脚本、workspace、CI 路由和 rustfmt hook 定向测试通过 |
|
||||
| 原生 GUI | `godot-plugin-verified-20260920/evidence/native-gui-smoke.log`:同一编辑器内返回 42/null、读取场景、增加节点后撤销、await、编译/运行错误、卸载、重连及再次卸载成功 |
|
||||
| Jenkins Windows 节点 | 2026-09-21 在修复分支按 `Genarrative-Agc-Windows-Build` 等价流程实跑(dry-run):preflight 报告 `cmake=3.31.6-msvc6`、`python=3.12.10`,Godot 原生载荷 staging 出 345600 字节 `agc_godot_editor.dll` 与元数据,NSIS `陶泥儿_0.1.88_x64-setup.exe`、签名、渠道清单和更新摘要全部生成 |
|
||||
| 多实例与只读安装 | 同目录 `parallel-live-modules.json`、`parallel-verified-summary.json`:两个编辑器同时使用不同缓存路径的官方 `~DLL`,均返回 42、确认卸载,安装原件只读且未变 |
|
||||
| 安装位置更新 | 同目录 `install-location-smoke.log`:旧连接卸载、新安装来源生成新代次和引用、返回 42、清理两代缓存;两个安装来源及原有工程文件均未变 |
|
||||
| 真实 Runner | 同目录 `runner-smoke-psapi.log`:standalone Runner 经正式执行/ACK 路径取得真实 Godot 回执,拒绝错误 ACK,接受正确 ACK,完成场景修改/撤销及断开;`runner-restart-before.log`、`runner-restart-after.log`:新 Runner 无需重新连接即可恢复原归属并清理旧桥 |
|
||||
| 资源与收尾 | 完整 Windows feature debug 构建通过;当前 `src-tauri/resources/plugins/agc-godot-editor` 仅含 manifest、入口、DLL、元数据、许可和来源六个文件,其 DLL 与实机测试一致;同目录 `final-cleanup.json` 确认工程及安装来源哈希未变、自建 GUI 正常退出 |
|
||||
|
||||
正式 Runner smoke 使用安装资源布局下的 debug 可执行文件,不等同于 NSIS 安装包 UI 验证。本次未运行安装包 UI smoke、真实 Provider 生成或远端 CI,未制作发布包或上传发布;其他 Godot 版本、.NET 编辑器与其他平台仍须各自验收。AGC 全量聚合门禁不在上述定向结果中。
|
||||
|
||||
### 复验入口
|
||||
|
||||
从仓库根运行,原生构建要求 Visual Studio 2022 C++ x64、CMake 3.25 及以上和 Python 3;通过 Visual Studio generator 自动选择完整编译环境。首次构建需访问固定的官方归档,校验后的依赖缓存支持离线复用;`build.ps1` 可用 `-CMake`、`-Python` 指定构建工具。先把 `AGC_GODOT_TEST_EXECUTABLE` 设置为待验证的标准 Godot 编辑器绝对路径;未设置时 headless 测试会跳过,不能视为实机通过。
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -File plugins/agc-godot-editor/native/gdextension/build.ps1
|
||||
python -X utf8 -B plugins/agc-godot-editor/native/gdextension/tests/test_dependencies.py
|
||||
node --test plugins/agc-godot-editor/native/gdextension/tests/native-smoke.test.mjs
|
||||
node --test plugins/agc-godot-editor/native/gdextension/tests/guide-examples.test.mjs
|
||||
cargo test --locked --manifest-path plugins/agc-godot-editor/native/godot-editor-bridge/Cargo.toml
|
||||
npm run agc:plugins:test
|
||||
cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --features cocos-editor-execute,unity-editor-execute,godot-editor-execute godot
|
||||
cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --features cocos-editor-execute,unity-editor-execute,godot-editor-execute plugin_host
|
||||
npx vitest run apps/ai-game-creator-shell/tests/pluginHost.test.ts packages/shared/src/contracts/gameCreationApp.test.ts --threads=false
|
||||
npm run typecheck --workspace @genarrative/ai-game-creator-shell
|
||||
npm run check:doc-index
|
||||
npm run check:encoding
|
||||
git diff --check
|
||||
```
|
||||
|
||||
真实 GUI 使用 `native/godot-editor-bridge/examples/live_smoke.rs`;安装位置变更使用同目录的 `install_location_smoke.rs`。二者要求显式传入自有可丢弃工程、已打开编辑器 PID、可信安装 DLL、工程外私有缓存及 `--allow-fixture-mutations`,具体参数见源码用法。多实例验证分别传入两个工程和 PID,通过 `live_smoke` 的 `--hold-ms` 让加载时间重叠,同时核验原生模块路径。Runner 复验须经正式长度前缀 RPC、完整 ACK 和私有配置恢复路径,不能用原生示例替代 Runner 证据。
|
||||
|
||||
### C++ 引导验收边界
|
||||
|
||||
官方 C++ 绑定版已通过实际 MSVC 构建、原生/指南 headless 回归、Rust 缓存与连接回归、受管资源 staging 校验,以及同一 Godot 4.7.2 GUI 进程中的首次加载、执行、卸载和重新聚焦后的重连。DLL 的导入依赖只有 KERNEL32,生成 SDK/编译缓存不进入 staging;重复构建命中同一载荷身份,损坏归档和改动过的依赖缓存拒绝构建。验证中首次创建的空工程由 Godot 补写版本特征,按编辑器初始化后的基线确认插件运行不修改原有工程文件。
|
||||
|
||||
GUI 自动聚焦若未触发扫描,会按原合同返回未派发错误;重新聚焦后再连接,不重放未知执行。此次 C++ 替换未重新运行完整 AGC 发布构建、真实 Provider 或安装包 UI 验证,前述旧版本证据不代替这些验收。
|
||||
@@ -3,10 +3,14 @@
|
||||
> 文档状态:`current`
|
||||
> 规范关系:承接 AGC 通用插件宿主与编辑器适配主规范
|
||||
|
||||
更新时间:`2026-09-18`
|
||||
更新时间:`2026-09-20`
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
常用操作指导随客户端审核 Skill pack 提供,入口为 `agc-unity-editor`。DirectProject 按需读取 [Skill 入口](../../apps/ai-game-creator-shell/src-tauri/resources/agc-skills/agc-unity-editor/SKILL.md) 和其常用操作参考;Runtime 的 `unity.editor.execute` 工具说明包含同一参考正文。指导覆盖查询、对象/组件、Prefab、资源、UI、保存与撤销,不新增专用操作工具,不改变执行授权。
|
||||
|
||||
指南示例在独立 Unity 6000.3.7f1 Mono 工程经真实 Attach 验证,覆盖查询、创建/修改、Undo、Prefab override 保存重开、Canvas 及播放切换;不据此推断 UI 视觉、第三方包或其他版本已验收。复验入口为 `native/unity-editor-bridge/tests/guide_examples.rs`:按文件头显式设置 fixture 的 helper、项目与 PID 环境变量后,运行 `cargo test --locked --manifest-path plugins/agc-unity-editor/native/unity-editor-bridge/Cargo.toml --test guide_examples -- --ignored --nocapture`;它直接提取随包指南代码,而非维护示例副本。
|
||||
|
||||
将 DotCraft.Unity 0.4.3 对应的 Attach 执行核心接入 AGC 现有插件系统,使当前 Unity 项目能够探测编辑器、建立连接、执行 C# 并获得真实结果。复用既有扩展列表、内置插件开关、权限、审计、EditorAdapter 和 Agent 工具通路。
|
||||
|
||||
首期只支持 Windows x64 的 Unity Mono Editor。连接不修改项目文件、不安装 UPM 包、不启动或关闭用户编辑器。不引入 DotCraft.Harness、另一套 Agent Runtime、MCP 服务或聊天界面;截图、热重载专用工具、macOS、Linux 和 Unity CoreCLR 不属于本次交付。
|
||||
@@ -18,7 +22,7 @@
|
||||
## 入口与行为合同
|
||||
|
||||
- Unity 项目以当前受控根目录中的普通 `ProjectSettings/ProjectVersion.txt`、`Assets/` 和 `Packages/` 识别;复用现有打开项目入口,不创建平行工作台。
|
||||
- 只有当前项目为 Unity、内置插件启用且平台适配器可用时才显示插件并向 Agent 暴露 Unity 工具。禁用或离开 Unity 项目后停止插件实例,执行入口再次检查开关和项目身份。
|
||||
- 插件列表、启动、面板、插件 RPC 和 Agent 工具暴露不按当前工程类型过滤;无项目或非 Unity 项目也沿用相同的内置开关及平台适配器规则。禁用会停止插件实例,跨工程类型切换保留插件管理能力并失效旧连接。前端按宿主列表中各插件自身状态投影自动启动,不因项目类型在 Cocos/Unity 之间二选一,也不自动展开面板。执行入口继续检查开关、权限和真实项目身份;工具可见不代表任意目录可作为 Unity 执行目标。
|
||||
- 探测只读取进程和项目身份。连接必须匹配规范化项目路径、PID、进程启动身份与实际握手;多个候选时失败,不选择任意实例。助手只接受受控项目、操作和代码,不接受任意可执行文件或 payload 路径。
|
||||
- 执行接收 UTF-8 C# 代码,最多 128 KiB,拒绝空值和 NUL。连接及执行有总期限,消息最多 2 MiB,并发执行直接拒绝,不积压写请求。
|
||||
- 成功仅由 Unity 真实执行回执决定;编译或运行错误返回结构化失败与脱敏诊断。主线程同步代码不承诺可硬中止。
|
||||
@@ -51,7 +55,7 @@ helper 使用一行一条 JSON 请求/响应,请求包含 `id`、`method`、`p
|
||||
| 条款 | 必须取得的证据 |
|
||||
| --- | --- |
|
||||
| 来源与构建 | 固定源码版本、许可、helper 构建和自包含发布检查 |
|
||||
| 插件接入 | manifest/协议测试,发现、启停、开关和项目级工具过滤测试 |
|
||||
| 插件接入 | manifest/协议测试,发现、启停、开关、无项目/跨工程类型的工具暴露和前端启动投影测试 |
|
||||
| 执行闭环 | helper 与原生适配器定向测试,Agent 参数及结果映射测试 |
|
||||
| 失败边界 | 跨项目、并发、超时、损坏回执、不确定阻断、重启插件不解除阻断测试 |
|
||||
| 分发 | Windows 构建脚本准备 helper,staging 仅包含目标平台运行文件及许可 |
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
# 【技术方案】AGC 后端框架整理与演进路线
|
||||
|
||||
更新时间:`2026-09-18`
|
||||
|
||||
## 结论
|
||||
|
||||
可以整理,而且不需要从零重写。当前 AGC 已经具备一套可复用的后端骨架,但它还不是一个可以直接部署的统一 AGC backend:共享 Runtime 目前是库级内核,本地 Tauri 持有项目执行事实,`server-rs` 持有云端平台业务,三者之间还需要 application adapter 和契约收口。现有骨架包括:
|
||||
|
||||
- `server-rs/crates/agent-runtime-core` 是不依赖产品和框架的 Runtime 执行内核。
|
||||
- `server-rs/crates/agent-runtime-orchestration` 是任务图、依赖波次和修复影响计算。
|
||||
- `server-rs/crates/platform-agent` 是 AGC 壳独立 path dependency 使用的游戏任务图、角色和游戏产物规则适配层;它被 `server-rs` 默认 workspace 排除,不能反向成为通用 Runtime 内核。
|
||||
- `apps/ai-game-creator-shell/src-tauri/src/agent`、`project`、`runner` 是本地 AGC 执行宿主。
|
||||
- `server-rs/crates/module-*`、`spacetime-module`、`spacetime-client`、`api-server` 和 `platform-*` 已覆盖领域、持久化、HTTP/BFF 和外部服务适配。
|
||||
|
||||
现在的主要问题是能力已经分布在多个入口,缺少一个对开发者可见的 AGC backend application facade 和统一的跨边界状态合同。目标应是整理边界和调用方向,逐步收拢入口,不新建第二套 Agent Runtime、第二套会话库或第二个业务真相。
|
||||
|
||||
## 目标
|
||||
|
||||
AGC backend 对外提供一个稳定的“项目开发运行时”能力面:接收用户意图,绑定项目身份,规划或执行 Agent Run,调用受控工具和平台能力,持久化可恢复状态,发布可观察事件,并把本地项目产物或云端资源结果返回给客户端。客户端本地宿主通过共享契约和平台 adapter 与云端交互,不直接把 `module-*` 当作本地 IO 层。
|
||||
|
||||
框架需要同时支持两种执行位置:
|
||||
|
||||
1. **本地宿主执行**:Agent 需要直接读写用户项目、启动本地进程、访问本地预览或编辑器时,在 Tauri Rust 宿主中执行。
|
||||
2. **云端控制面执行**:认证、账户、模型目录、编辑器资源、异步生成、计费、快照上传和公开 API 在 `server-rs` 中执行。
|
||||
|
||||
两者共享领域合同、Runtime 语义和错误分类,但不共享不适合跨进程的本地副作用实现。
|
||||
|
||||
## 非目标
|
||||
|
||||
- 不把 Tauri 本地文件、进程、浏览器或 Codex app-server 搬进云端。
|
||||
- 不把 `api-server` 变成任意代码执行器,也不让前端直接访问 SpacetimeDB。
|
||||
- 不引入 LangChain、AutoGen、OpenAI Agents SDK sidecar 或另一套调度器作为 AGC 核心。
|
||||
- 不恢复已退役的旧创作入口、旧作品架或旧后端服务。
|
||||
- 不在本次整理中改变现有公开 API、SpacetimeDB 表字段、OpenAPI 或本地项目文件格式。
|
||||
|
||||
## 逻辑分层
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
UI[AGC Web UI / Tauri commands]
|
||||
HOST[AGC Local Host\nTauri Rust]
|
||||
CORE[Shared Agent Runtime\nagent-runtime-core]
|
||||
ORCH[Task Graph\nagent-runtime-orchestration]
|
||||
CONTRACT[Shared contracts\nshared-contracts]
|
||||
DOMAIN[Cloud domain modules\nmodule-*]
|
||||
API[Cloud Control Plane\napi-server Axum]
|
||||
DATA[Data plane\nspacetime-client -> SpacetimeDB]
|
||||
PLATFORM[Platform adapters\nplatform-llm / image / oss / auth / ...]
|
||||
PROJECT[Local Project Plane\nfiles / manifest / JSONL / locks / checkpoints]
|
||||
|
||||
UI --> HOST
|
||||
UI --> API
|
||||
HOST --> CORE
|
||||
HOST --> ORCH
|
||||
HOST --> CONTRACT
|
||||
HOST --> PROJECT
|
||||
API --> CONTRACT
|
||||
API --> DOMAIN
|
||||
API --> DATA
|
||||
API --> PLATFORM
|
||||
PLATFORM --> DATA
|
||||
```
|
||||
|
||||
这张图是调用方向,不表示所有层都必须变成新的 crate。当前 `api-server` 不依赖或执行本地 `agent-runtime-core`/`agent-runtime-orchestration`;如果未来明确引入云端 Runner,再单独增加云端 Runtime host,不把本地项目 Run 事实迁移到 API handler。现有目录已经能承载大部分边界,先用 facade、trait 和契约测试固化边界,再决定是否需要物理拆 crate。
|
||||
|
||||
### 1. Shared Runtime kernel
|
||||
|
||||
`agent-runtime-core` 只负责通用运行事实和确定性决策:Run、Action、Observation、Tool execution、Completion、Recovery、Provider contract、Capability registry 和 Runtime event。它不能依赖 Tauri、Axum、SpacetimeDB、具体提示词、权限实现或 AGC 业务表。
|
||||
|
||||
`agent-runtime-orchestration` 只负责任务图:Agent catalog 校验、依赖关系、ready task、wave 和下游修复影响。它不持有线程、Provider、工具、数据库或产品持久化。
|
||||
|
||||
这两层是 AGC 与其他未来 Agent 宿主共享的内核,不能把 AGC 特有规则反向塞回去。
|
||||
|
||||
### 2. Domain and contract layer
|
||||
|
||||
领域规则留在现有 `module-*`:
|
||||
|
||||
| 领域 | 当前承载位置 | 负责内容 |
|
||||
| --- | --- | --- |
|
||||
| 主站画布 Agent 会话 | `module-editor-agent` | 画布会话归属、标题、消息元数据、领域校验;不是 DirectProject 本地会话事实源 |
|
||||
| Runtime / AI task | `module-runtime`、`module-ai` | Runtime 设置、模型目录、AI task 状态和输入校验 |
|
||||
| 编辑器项目和资源 | `module-assets`、`spacetime-module` 相关 storage | 项目、资源、版本绑定、生成任务和事务规则 |
|
||||
| 认证和账户 | `module-auth`、`platform-auth` | 身份、会话和认证平台适配 |
|
||||
|
||||
跨端 DTO、公开请求/响应和错误码留在 `shared-contracts`。目标边界是领域层不发 HTTP、不读本地文件、不调用 LLM、不直接拿 OSS 客户端;当前部分 `module-*` 仍通过 feature 或 service 依赖平台 crate,这属于后续收口债务,不应继续扩大。
|
||||
|
||||
### 3. Local AGC host
|
||||
|
||||
`apps/ai-game-creator-shell/src-tauri` 是本地 backend,不只是 UI 的 IPC 薄壳。普通 Tauri command 只负责鉴权、确认和 UI bridge;真正的 LLM、工具循环、队列和恢复由独立的 `--agent-runner` 执行者完成。它负责:
|
||||
|
||||
- `agent`:DirectProject、Runtime driver、Runtime protocol、工具桥、Codex app-server/CLI、Provider handoff 和恢复。
|
||||
- `project`:项目根边界、manifest、JSONL 会话、checkpoint、资源编辑、文件读写、项目写锁和版本替换。
|
||||
- `runner`:跨窗口共享的 Agent Runner、loopback 协议、owner/watchdog、连接和生命周期。
|
||||
- `browser`、`preview`、`process_session`:受控预览、浏览器试玩、本地进程会话及证据。
|
||||
- `editor_adapter`、`plugin_host`:编辑器桥接和插件能力,不向 Agent 暴露原始 pipe、句柄或未绑定项目身份的执行面。
|
||||
- `platform_session`、`http_client`:平台身份/凭据和云端 API 调用。
|
||||
|
||||
本地宿主可以复用共享 Runtime crate 和 `platform-agent`,但项目文件、资源权限、完成门、Prompt Bundle、DirectProject/Planning V2 和 Runner IPC 都属于 AGC adapter/host,不能迁回 Runtime core。
|
||||
|
||||
`main.rs`、`agent.rs` 和命令注册只应是组合层。新的业务规则应进入对应功能域或 Runtime contract,不能继续堆进入口文件。
|
||||
|
||||
### 4. Cloud control plane
|
||||
|
||||
`api-server` 是云端 HTTP/SSE/BFF 和跨模块编排层。它不持有本地 `--agent-runner` 的项目 Run 事实;除非未来明确引入云端 Runner,否则云端只持有平台业务、异步 operation 和可公开投影。现有入口包括:
|
||||
|
||||
- `/api/ai/tasks`:AI task 生命周期。
|
||||
- `/api/editor/*` 与 `/api/external/v1/editor/*`:编辑器项目、资源和生成操作。
|
||||
- `/api/agc/project-snapshots/*`:AGC 项目文件与 manifest 快照上传。
|
||||
- `/admin/api/agc-models`:AGC 模型目录管理。
|
||||
- 编辑器 Agent 会话、外部生成任务、运行设置和诊断/追踪相关接口。
|
||||
|
||||
这些路由可以继续按领域模块存在,但需要分成两个语义清晰的 facade:
|
||||
|
||||
- **本地 host coordinator**:在 Tauri 宿主内统一 `start_run`、`resume_run`、`cancel_run`、`get_run` 和本地事件投影,事实源仍是项目 `.agent/runtime/**` 与 Runner。
|
||||
- **云端 application service**:在 `api-server` 统一 `submit_operation`、`get_operation`、`reconcile_operation`、AI task、外部生成和快照调用,复用现有领域 service、队列和 SpacetimeDB procedure。
|
||||
|
||||
两者可以共享 DTO、错误分类和 correlation 字段,但当前不新增统一的云端 `/runs` API,也不把本地 Run 伪装成云端持久化记录。
|
||||
|
||||
### 5. Platform adapters
|
||||
|
||||
LLM、图片/音频/视频、OSS、认证、语音和编辑器外部服务继续放在 `platform-*`。适配器只返回受约束的 Provider/Job/Asset 结果;重试、幂等、扣费、结果落库和公开错误映射由上层 application/domain contract 决定。
|
||||
|
||||
## 数据真相和存储边界
|
||||
|
||||
### 本地项目平面
|
||||
|
||||
以下事实属于当前授权项目,由本地宿主负责读写和恢复:
|
||||
|
||||
- 项目源文件、`manifest`、版本和资源绑定的本地投影。
|
||||
- `.agent` 下的会话 JSONL、Runtime journal、action/observation、checkpoint 和诊断摘要。
|
||||
- 项目写锁、Runner owner、进程会话、预览注册和浏览器证据。
|
||||
|
||||
本地写入必须经过项目身份校验、项目根边界、普通文件/reparse point 检查、写锁和现有权限策略。云端 API 不直接读写用户项目目录。
|
||||
|
||||
### 云端业务平面
|
||||
|
||||
以下事实属于服务端:
|
||||
|
||||
- 登录身份、refresh session、钱包/计费和模型目录。
|
||||
- 编辑器项目、资源、版本绑定、生成任务、任务事件和结果引用。
|
||||
- 错误报告、追踪事件和公开 read model;快照当前以受控 OSS manifest/对象键、审计和诊断能力为主,不假定已经存在 SpacetimeDB 快照元数据表。
|
||||
|
||||
当前 SpacetimeDB 没有通用的 AGC Agent Run/Action/Event/Confirmation/Plan 表;`runtime_snapshot` 不能直接当作 AGC Agent Runtime 持久化。若未来把 Run 迁到云端,必须另立清晰的持久化合同;本路线默认本地 `.agent/runtime/**` 继续是 DirectProject 的事实源。
|
||||
|
||||
SpacetimeDB 表、reducer、procedure 和事务 adapter 只留在 `spacetime-module`;服务端访问统一走 `spacetime-client` facade。
|
||||
|
||||
### OSS / 大对象平面
|
||||
|
||||
会话正文、项目快照文件和媒体大对象继续按现有合同进入 OSS;SpacetimeDB 只保存归属、对象键、版本、状态和必要摘要。OSS key 必须由服务端根据已验证的身份/项目 ID 生成,客户端不能把任意对象键当作归属证明。
|
||||
|
||||
## 统一执行合同
|
||||
|
||||
本地 Run 和云端异步操作可以有不同实现,但公开状态应映射到同一组语义:
|
||||
|
||||
```text
|
||||
intent
|
||||
-> accepted
|
||||
-> queued
|
||||
-> running
|
||||
-> waiting (user / confirmation / provider retry / external job)
|
||||
-> succeeded | failed | cancelled | needs-reconciliation
|
||||
```
|
||||
|
||||
- `accepted` 表示请求已经被持久化并得到稳定身份,不表示 child 已经开始执行。
|
||||
- `running` 必须由真正持有执行权的 Runner/worker 写入 started 事实后才能公开。
|
||||
- `waiting` 必须有可恢复的原因和下一步信息,不能由前端计时器伪造。
|
||||
- 外部结果未知、不能安全重放时进入 `needs-reconciliation`,禁止自动重放或伪造成功。
|
||||
- 取消、失败、恢复、重试和最终回复都必须保留同一 Run/operation 身份及可追踪的 event 顺序。
|
||||
|
||||
现有 `RunStatus`、`ActionStatus`、`ObservationStatus`、AI task 状态、ExternalGenerationJob 状态和项目生成账本可以继续各自保留;application facade 只提供上述语义映射,不强行把它们合并成一张跨域表。
|
||||
|
||||
## 核心调用链
|
||||
|
||||
### 本地 DirectProject
|
||||
|
||||
```text
|
||||
UI
|
||||
-> Tauri command
|
||||
-> DirectProject / Runtime driver
|
||||
-> Runner / Codex app-server
|
||||
-> Runtime tool policy
|
||||
-> project owner + write lock
|
||||
-> local files / manifest / JSONL / checkpoint
|
||||
-> Runtime event projection
|
||||
-> UI
|
||||
```
|
||||
|
||||
工具执行失败时,属于可修复的构建、验证或试玩错误,可以把脱敏上下文反馈给同一 LLM 回合并有界重试;鉴权、项目身份、权限、传输断开、取消、历史损坏和付费操作不确定等边界必须失败关闭。
|
||||
|
||||
### 云端资源/生成操作
|
||||
|
||||
```text
|
||||
AGC tool or UI
|
||||
-> api-server route
|
||||
-> auth + project ownership + idempotency
|
||||
-> module-* validation
|
||||
-> spacetime-client facade
|
||||
-> durable operation / queue
|
||||
-> platform-* provider
|
||||
-> OSS result
|
||||
-> SpacetimeDB result projection
|
||||
-> API polling/SSE
|
||||
```
|
||||
|
||||
计费、外部 job、结果安装和重试必须围绕 durable operation 编排,不能把 Provider 返回成功当成业务完成的唯一证据。
|
||||
|
||||
### 项目快照
|
||||
|
||||
```text
|
||||
Local project snapshot
|
||||
-> authenticated /api/agc/project-snapshots/*
|
||||
-> body-size and file contract checks
|
||||
-> object storage
|
||||
-> snapshot metadata / diagnostic projection
|
||||
```
|
||||
|
||||
快照上传用于备份和诊断,不改变本地项目作为开发态文件真相的职责。
|
||||
|
||||
## 边界规则
|
||||
|
||||
1. **Runtime 与宿主分离**:核心 Runtime 只做状态决策;Tauri host、Runner 和 server worker 提供执行、持久化、时间和能力实现。
|
||||
2. **领域与 IO 分离**:`module-*` 不依赖 Axum、Tauri、Provider 或 OSS;HTTP handler 不复制领域校验。
|
||||
3. **文件系统单一入口**:任何 Agent 文件读写都经过项目 scope、写锁和受控工具;不能在 API 或前端加旁路写入。
|
||||
4. **外部服务单一入口**:LLM、生成、OSS、认证等外部调用只经 `platform-*`,不能在业务 handler 里散落裸 HTTP。
|
||||
5. **数据访问单一入口**:`api-server` 和本地需要的云端调用使用 `spacetime-client` facade,不直接拼 SpacetimeDB 请求。
|
||||
6. **前端只消费投影**:前端不推导正式业务状态,不直接访问本地项目数据库或 SpacetimeDB。
|
||||
7. **身份与凭据分离**:同一身份的 access token 轮换不使在途 Run 失效;换号、退出或 origin 变化必须使旧 Run 停止后续副作用。
|
||||
8. **可恢复优先**:持久动作、异步生成、Runner 续跑、外部结果回填和最终状态写入必须有稳定 ID、版本/CAS 或幂等键。
|
||||
|
||||
## 当前缺口
|
||||
|
||||
### 缺少 AGC application facade
|
||||
|
||||
能力散落在 `agent`、`editor_project.rs`、`external_editor_api.rs`、`ai_tasks.rs`、快照路由和生成队列中。它们各自可用,但调用方难以判断一项用户意图应该走本地 Run、云端 task 还是外部 generation operation。
|
||||
|
||||
**整理方向**:先建立逻辑 facade 和 capability catalog,保留现有路由作为 adapter;只有当两个以上入口共享同一业务流程时,才下沉 application service。
|
||||
|
||||
### 本地和云端状态语义重复
|
||||
|
||||
本地 Runtime、AI task、ExternalGenerationJob 和项目资源 operation 都有自己的 accepted/running/failed/retry 表达。状态不能强行合表,但需要统一事件字段:`subject_id`、`run_or_operation_id`、`stage`、`revision`、`occurred_at`、`retryable`、`public_summary` 和 `correlation_id`。
|
||||
|
||||
### 组合层偏重
|
||||
|
||||
AGC Tauri 的 `main.rs`、`agent.rs` 和部分命令模块已经包含大量注册与兼容出口。后续工作应按功能域继续拆分,但每次只移动一个边界并保持公开导出、命令名称和测试合同不变。
|
||||
|
||||
### 云端大文件模块偏重
|
||||
|
||||
`api-server` 的编辑器项目、External Editor API 和生成队列承担了较多请求解析、领域校验、计费、队列和结果投影。后续应把领域输入校验和 operation service 继续下沉到 `module-*`/application 层,handler 只保留鉴权、解析、调用和响应映射。
|
||||
|
||||
### 契约目录需要集中
|
||||
|
||||
AGC 本地 IPC、Runner 协议、Runtime snapshot、云端 API、SpacetimeDB procedure 和 OSS key 规则目前分布在代码及专题文档中。应建立一个只读的 AGC capability/contract catalog,列出入口、调用方、状态源、权限、幂等键、恢复策略和证据位置;它不是新的运行时注册表。
|
||||
|
||||
## 演进路线
|
||||
|
||||
### 第一阶段:框架定界
|
||||
|
||||
- 将本文件作为 AGC backend 主规范。
|
||||
- 维护 capability catalog,给每个现有入口标注 `local-host`、`cloud-control-plane` 或 `shared-runtime`。
|
||||
- 为本地 Run、云端 task、外部 generation 和快照上传补齐统一 correlation/idempotency 字段的映射表。
|
||||
- 不改 API、schema 和文件格式。
|
||||
|
||||
### 第二阶段:统一 application facade
|
||||
|
||||
- 在现有 `api-server` modules 之上增加薄的 AGC application service/facade。
|
||||
- 先收拢 Run 查询、事件查询、取消、恢复和 operation reconcile;生成业务仍复用现有队列和平台适配器。
|
||||
- 本地 Tauri 侧将 DirectProject、Runtime driver 和 Runner 的入口分成 `command adapter -> application coordinator -> runtime host` 三层。
|
||||
- 用契约测试锁定调用方向,防止前端或 handler 绕过 domain/client facade。
|
||||
|
||||
### 第三阶段:持久化与恢复收口
|
||||
|
||||
- 为本地 Runtime journal、云端 AI task、外部 generation job 和项目资源 operation 逐项定义 accepted/running/waiting/terminal 的写入点。
|
||||
- 补齐重启、重复提交、迟到回调、同身份 token 轮换、换号和 unknown outcome 的恢复测试。
|
||||
- 继续保持本地项目文件与云端业务数据的双平面,不做隐式云同步。
|
||||
|
||||
### 第四阶段:能力插件化
|
||||
|
||||
- 以 capability contract 接入编辑器 adapter、图像/音频/视频生成、浏览器试玩和 UI workflow。
|
||||
- 每个能力声明输入约束、权限、写入范围、计费/异步语义、产物身份和验证证据。
|
||||
- Cocos 等编辑器桥接继续通过通用 plugin host,不把编辑器进程控制细节泄漏给 Runtime core。
|
||||
|
||||
### 第五阶段:观测和发布门禁
|
||||
|
||||
- 统一 Run/operation 的 request ID、correlation ID、client marker、错误分类、诊断事件和公开摘要。
|
||||
- 建立 AGC backend smoke:健康检查、鉴权、最小 Run、工具拒绝、快照体积边界、异步生成轮询和恢复。
|
||||
- 把真实 Provider、登录态、Windows Runner 和本地预览作为单独运行时证据,不用静态检查替代。
|
||||
|
||||
## 验收标准与证据
|
||||
|
||||
| 条款 | 验收方式 | 证据 |
|
||||
| --- | --- | --- |
|
||||
| 共享 Runtime 不依赖产品 IO | `cargo check/test -p agent-runtime-core -p agent-runtime-orchestration` 与依赖检查 | crate 依赖和测试输出 |
|
||||
| 本地工具只能在授权项目内执行 | AGC Rust 项目 scope、symlink/reparse、写锁和权限定向测试 | `apps/ai-game-creator-shell/src-tauri` 测试报告 |
|
||||
| 云端路由不绕过 domain/client | api-server 定向测试和源码依赖检查 | handler、module、spacetime-client 调用证据 |
|
||||
| 重复提交不产生重复副作用 | 本地 Run、AI task、ExternalGenerationJob 和生成 operation 幂等测试 | 稳定 ID/receipt/CAS 断言 |
|
||||
| 未知外部结果失败关闭 | provider/queue/reconcile 测试 | `needs-reconciliation` 终态证据 |
|
||||
| 认证续期不误杀在途 Run | session 与 Runtime 定向测试 | 同身份凭据轮换、换号失效证据 |
|
||||
| 快照上传受控 | `/api/agc/project-snapshots/*` API smoke | 鉴权、体积、项目归属和 OSS key 断言 |
|
||||
| 公开状态可追踪 | request/correlation/client marker 与错误摘要测试 | tracking、诊断事件和公开 response |
|
||||
| 文档与代码口径一致 | `npm run check:doc-index`、`npm run check:encoding`、`git diff --check` | 命令输出 |
|
||||
|
||||
## 决策
|
||||
|
||||
1. AGC backend 采用“共享 Runtime 内核 + 本地执行宿主 + 云端控制面 + 领域/平台适配器”的逻辑框架;`platform-agent` 继续承载 AGC 游戏特化规则,不能被当作通用内核。
|
||||
2. 先整理本地 host coordinator、云端 application service、contract catalog 和测试边界,再决定是否需要新增物理 crate;当前不新建平行 `agc-runtime`。
|
||||
3. 本地项目文件和云端业务数据保持双平面,快照是受控上传能力,不是开发态主数据同步。
|
||||
4. `agent-runtime-core` 与 `agent-runtime-orchestration` 保持产品无关;AGC 规则进入 host、application 或 `module-*`。
|
||||
5. 公开 API、SpacetimeDB schema、生成绑定和现有本地文件合同在后续实现阶段保持兼容,任何 breaking change 单独走 SDD 和迁移评审。
|
||||
|
||||
## 未决问题
|
||||
|
||||
- AGC application facade 第一批要覆盖哪些调用方:仅客户端本地 Run,还是同时纳入外部编辑器 API。
|
||||
- 云端是否需要持久化 AGC Run 元数据,还是继续以本地 Run 为主、云端只持久化资源和异步 operation。
|
||||
- capability catalog 最终作为文档、生成的共享 DTO,还是仅作为测试清单;在未证明需要运行时发现前,不把它做成新的动态插件注册中心。
|
||||
- 是否有明确的云端 Runner 需求;在没有多租户后台执行需求前,不把本地 Runner 迁移到服务端。
|
||||
@@ -1,13 +1,21 @@
|
||||
# AGC 客户端更新检查与下载
|
||||
|
||||
更新时间:`2026-09-17`
|
||||
更新时间:`2026-09-20`
|
||||
|
||||
本文件是 AGC 客户端自动更新的主规范:更新能力由 Tauri 官方插件 `tauri-plugin-updater` 承担,并按下文渠道分发。
|
||||
|
||||
## 目标
|
||||
|
||||
- 客户端自动更新改用 Tauri 官方 `tauri-plugin-updater`:清单请求、版本比较、更新包下载、签名校验、安装与退出全部在原生侧完成;前端只负责触发、展示和渠道选择。
|
||||
- 更新按渠道分发。当前渠道集合为 `dev-win`(Windows x64)与 `dev-mac`(macOS);构建管线按渠道产出并上传清单,客户端只读取自己渠道的清单。
|
||||
- 更新按渠道分发。渠道为 `dev`、`release` 或自定义名称;Windows/macOS 是独立的系统维度,每个渠道分别维护各系统已发布的版本、清单和安装包。客户端只读取构建时确定的渠道及系统对应的清单。
|
||||
|
||||
## 渠道与网站配置合同
|
||||
|
||||
- 构建参数 `AGC_UPDATE_CHANNEL` 默认 `dev`,支持 `release` 和自定义小写名称;名称符合 `[a-z][a-z0-9-]{0,31}`,不能以连字符结尾,不能为 `win/mac/windows/macos/darwin/linux` 或以 `-win/-mac` 结尾。构建目标独立决定系统和架构。
|
||||
- 为延续已发布客户端地址,OSS 继续使用 `agc/<channel>-win/` 和 `agc/<channel>-mac/` 作为物理分区;`dev-win/dev-mac` 是分区键,不是可填写的渠道。每个分区独立维护 `latest.json` 与版本目录,发布 release 不覆盖 dev。旧 `agc/latest.json` 迁移桥与其版本高水位仅属于 dev 的 Windows 分区。
|
||||
- 网站由服务端配置 `GENARRATIVE_CLIENT_DOWNLOAD_CHANNEL` 选择检测渠道,默认 `dev`;更改后重启 API 服务生效。`GET /api/client-downloads` 返回该渠道 Windows/macOS 的真实已发布版本。请求参数不能覆盖配置或指定 URL;配置非法时失败关闭,不悄悄改读 dev。
|
||||
- 同一渠道中不同系统仍可具有不同 release 版本;未发布的系统隐藏,单系统失败不影响另一系统。不会从其他渠道补齐缺失版本。
|
||||
- 验收必须覆盖 dev/release/自定义渠道各自端点和对象地址、独立版本、非法名称、旧 dev 地址延续、release 不写旧迁移桥、网站配置贯通、跨渠道链接拒绝和部分失败。
|
||||
- 更新链路的信任来源从「清单里的 sha256 + 受信域名」升级为「发布签名 + 受信域名」:清单里的 `signature` 由构建期私钥生成,客户端用内置公钥校验,校验不过就拒绝安装。
|
||||
|
||||
## 非目标
|
||||
@@ -30,12 +38,24 @@
|
||||
|
||||
## 必须成立的行为
|
||||
|
||||
### 官网下载与客户端服务地址
|
||||
|
||||
- 官网首页提供无需登录的「下载客户端」入口,桌面和移动视口均可访问;入口打开独立下载面板,复用平台按钮、弹窗和状态组件。
|
||||
- 每次打开下载面板请求同源公开 `GET /api/client-downloads`,后端并行读取配置渠道的 `<channel>-win/latest.json` 与 `<channel>-mac/latest.json` 并汇总已发布平台,网页和接口均禁用缓存。平台列表与每项版本完全来自清单;不在网页写死版本或猜测文件名。
|
||||
- 接口返回 `{ downloads: [{ platform, architecture, version, downloadUrl }], unavailablePlatforms: [] }`,平台为 `windows` / `macos`,架构为 `x86_64` / `aarch64`;Windows 仅支持 x86_64,Mac 按清单实际提供的架构展示 Apple Silicon / Intel 下载项。DTO 由 `shared-contracts` 与 `packages/shared` 对齐,不接受请求参数指定上游 URL,不触及 SpacetimeDB。
|
||||
- 渠道清单新增可选 `downloads` 字典,键与 updater 平台键一致,值为 `{ url }`,只登记首装包。Windows `.exe` 可同时用于首装和更新;macOS 首装必须为 `.dmg`,不得将 `.app.tar.gz` 当首装包。已发布且没有 `downloads` 字段的 Windows 清单可读取既有 `platforms.windows-x86_64.url`;Mac 没有首装元数据时隐藏,不推导 DMG 地址。
|
||||
- 渠道 404 视为尚未发布并隐藏该平台;两端均未发布时显示空状态。单渠道请求失败、超时或格式非法时保留另一端有效下载项,同时提示部分平台暂不可用并允许重试;没有任何有效项且存在失败时返回可读 502。关闭面板取消请求,迟到响应不能覆盖下一次打开的状态。
|
||||
- 每个上游请求总超时 10 秒、响应体上限 128 KiB、不跟随重定向、不附加用户凭据;返回的链接仅接受固定 OSS 来源、对应 `/agc/<channel>-win|mac/<version>/` 分区下的 HTTPS `.exe` / `.dmg` 对象,架构键必须属于对应系统。不提供陈旧、未知来源或版本不匹配的下载地址,不泄露上游正文。
|
||||
- AGC 本地 debug 态保留服务器选择,可选择 `release`、`dev` 或自定义 HTTPS / 本机 HTTP 地址;正式打包产物隐藏服务器选择。打包产物的平台服务地址跟随构建渠道:`release` 渠道连接 `https://www.genarrative.world`,`dev` 渠道连接 `https://dev.genarrative.world`,不会被旧的本地服务器偏好覆盖。自定义 LLM 配置不属于平台服务器选择。
|
||||
- access token 与 origin 一起保存;登录、刷新、退出和原生会话回写都使用同一个已冻结的 origin。没有 origin 的旧 token 一律清除,因为旧版可以单独修改服务器偏好,偏好不能证明 token 来源。
|
||||
- 验收覆盖 debug 服务器选择与自定义地址、release/dev 渠道服务地址、origin 隔离、旧服务器偏好、首页入口挂载、动态最新版本链接、清单失败与重试、关闭取消、桌面和移动布局,以及公开清单和安装包的真实可读性。
|
||||
|
||||
### 正常路径
|
||||
|
||||
- 正式包启动时检查一次渠道清单;仅当清单版本高于当前版本时显示更新提示,提示包含目标版本与发布说明。
|
||||
- 用户确认后下载更新包:下载期间显示进度与已下载字节数;下载完成后按平台安装。
|
||||
- Windows 使用静默安装模式(NSIS `quiet`),安装启动成功后客户端退出并由安装程序重启新版本;macOS 由客户端在安装完成后重启进程接管新版本。
|
||||
- 渠道在构建期确定并烘焙进产物:`dev-win` 产物只读 `dev-win` 清单,`dev-mac` 产物只读 `dev-mac` 清单,同一份二进制不会在运行期跨渠道切换。
|
||||
- 渠道与系统在构建期确定并烘焙进产物:如 dev 的 Windows 产物只读 `dev-win` 分区,release 的 Mac 产物只读 `release-mac` 分区,同一份二进制不会在运行期跨渠道切换。
|
||||
- 开发态(`npm run agc` / `agc:serve` 由 Vite dev server 提供前端)不检查更新、不显示更新入口,也不下载任何更新包。
|
||||
|
||||
### 失败、重试与幂等
|
||||
@@ -66,23 +86,26 @@
|
||||
"signature": "<.sig 文件内容>",
|
||||
"url": "https://<oss>/agc/dev-win/0.1.48/<安装包文件名>"
|
||||
}
|
||||
},
|
||||
"downloads": {
|
||||
"windows-x86_64": { "url": "https://<oss>/agc/dev-win/0.1.48/<安装包文件名>" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 渠道与平台映射:
|
||||
- 渠道与平台分区映射(`<channel>` 为 dev、release 或自定义名称):
|
||||
|
||||
| 渠道 | 构建目标 | 清单平台键 | 更新包 | 清单地址 |
|
||||
| 系统 | 构建目标 | 清单平台键 | 更新包 | 清单地址 |
|
||||
| --------- | ------------------------ | ---------------------------------------------- | ------------------------ | ------------------------------------ |
|
||||
| `dev-win` | `x86_64-pc-windows-msvc` | `windows-x86_64` | NSIS `.exe` + `.exe.sig` | `<OSS base>/agc/dev-win/latest.json` |
|
||||
| `dev-mac` | `aarch64-apple-darwin` 或 `x86_64-apple-darwin` | 对应 `darwin-aarch64` 或 `darwin-x86_64` | `*.app.tar.gz` + `.sig` | `<OSS base>/agc/dev-mac/latest.json` |
|
||||
| Windows | `x86_64-pc-windows-msvc` | `windows-x86_64` | NSIS `.exe` + `.exe.sig` | `<OSS base>/agc/<channel>-win/latest.json` |
|
||||
| macOS | `universal-apple-darwin` | `darwin-aarch64` + `darwin-x86_64`(同一对象) | `*.app.tar.gz` + `.sig` | `<OSS base>/agc/<channel>-mac/latest.json` |
|
||||
|
||||
- 对象布局:清单固定写成 `agc/<channel>/latest.json`;安装包与签名写成 `agc/<channel>/<version>/<file>` 与 `<file>.sig`。
|
||||
- macOS 当前采用单架构包:Apple Silicon 使用 `aarch64-apple-darwin`,Intel 使用 `x86_64-apple-darwin`;每次生成的清单只登记本次实际构建的架构,不把单架构原生 Codex 资源挂到另一架构。`universal-apple-darwin` 在版本读取/写入、构建和清单生成之前拒绝。
|
||||
- 渠道清单以实际运行架构为键。两种单架构构建不可轮流覆盖同一个 `latest.json` 并宣称双架构均可更新;当前不实现跨构建合并,Intel 发布需先完成其构建验证与多架构清单发布方案。
|
||||
- 对象布局:清单固定写成 `agc/<channel>-win|mac/latest.json`;安装包与签名写成同一分区的 `<version>/<file>` 与 `<file>.sig`。
|
||||
- macOS 正式交付使用 universal 主程序:两个平台键指向同一个 `.app.tar.gz` 与签名,一份产物同时服务 Apple Silicon 与 Intel。单架构目标(`aarch64-apple-darwin` / `x86_64-apple-darwin`)只用于本机诊断,不登记正式分区清单——单架构构建不可轮流覆盖同一个 `latest.json` 并宣称双架构均可更新。
|
||||
- universal 主程序同时携带分目录的 arm64/x64 原生 Codex 组件:每个组件保持上游单架构布局与独立 SHA-256 清单,运行中的主程序切片只选择同架构目录,不得把两套原生包的元数据或辅助程序混装。
|
||||
- 构建期要求:打开 `bundle.createUpdaterArtifacts` 以生成 `.sig`;构建环境提供签名私钥与密码(私钥内容不得入库);公钥写入客户端配置。公钥在首个带更新能力的版本发布后不可更换,更换等于放弃自动更新(只能手动重装)。
|
||||
- 版本递增按渠道独立进行:发布脚本读取该渠道远端 `latest.json` 的 `version`,与本地版本取较高者递增 patch;两个渠道的版本号互不影响。
|
||||
- 版本高水位:发布脚本取「渠道清单版本」与「旧协议迁移指针版本」(迁移窗口内)中的较大值再递增。只看渠道清单会在渠道启用初期把版本链改小 —— 2026-09-17 首次渠道发布即把旧指针的 0.1.57 退回 0.1.48,随后以显式 0.1.60 纠偏;迁移窗口结束(旧指针 404)后自动只剩渠道清单,`dev-mac` 不参与旧指针比较。
|
||||
- 版本递增按渠道及系统分区独立进行:发布脚本读取该分区远端 `latest.json` 的 `version`,与本地版本取较高者递增 patch;不同分区的远端版本互不影响。
|
||||
- 版本高水位:仅 dev 的 Windows 分区在迁移窗口内取「分区清单版本」与「旧协议迁移指针版本」较大值再递增,避免已发布旧客户端版本倒退。迁移窗口结束(旧指针 404)后只读分区清单;release、自定义渠道与所有 Mac 分区均不参与旧指针比较。
|
||||
- 迁移(旧协议 → 渠道清单):
|
||||
- 迁移起点:已发布客户端(含当前线上版本)内置自研清单地址 `agc/latest.json`(sha256 格式),下载与安装由自研 Rust 命令完成。
|
||||
- 迁移策略见「未决问题与决策」。迁移完成后,自研清单解析、下载命令、下载进度事件以及为此放行的 CSP / HTTP 白名单条目按「四不写」整条删除,不留兼容分支与墓碑说明。
|
||||
@@ -91,22 +114,39 @@
|
||||
|
||||
- 发布入口:`npm run ai-game-creator-shell:release:upload`(构建 + 按渠道上传);仅构建不发布的 smoke 使用 `--no-bundle` 分支,不读远端版本、不改版本、不生成清单。
|
||||
- 发布入口只解析一次目标,优先级为 CLI `--target value` / `--target=value` / `-t value`、`AGC_BUILD_TARGET`、Windows 默认值;重复/空目标与不支持目标失败关闭。版本高水位、构建 feature/渠道端点、bundle 路径、产物后缀、清单平台键及摘要必须消费同一个发布上下文,不能分别回读默认目标。
|
||||
- 渠道由构建参数显式指定,并按目标平台校验:Windows 目标只允许 `dev-win`,macOS 目标只允许 `dev-mac`;未显式指定时按目标平台取默认渠道。
|
||||
- 渠道由 `AGC_UPDATE_CHANNEL` 显式指定,默认 dev;Windows 与 macOS 目标均支持 dev、release 和自定义渠道,目标校验独立进行。
|
||||
- 定时调度只在本轮到达的提交包含 AGC 相关路径(客户端、共享包、`server-rs/crates`、AGC 插件、桌面壳图标、根依赖清单)时才触发渠道发布;纯文档或流水线自身的提交只跑 Full Build,不推高客户端版本号。判定失败或勾选强制触发时按"需要发布"处理。
|
||||
- 更新摘要自动生成:发布脚本用渠道清单里的 `commit` 字段(上一次发布的提交)到本次提交之间、且只覆盖客户端相关路径的提交列表生成 `notes`(每条 `- 提交标题(短 SHA)`,最多 12 条、主题 80 字、整体 900 字,超出折叠或截断),同时写入旧协议清单的 `releaseNotes` 和归档文件 `release-notes.txt`。`AGC_UPDATE_RELEASE_NOTES` 非空时以手动文案为准;无法判定起点(缺少上次 `commit` 或本地没有该提交)时不写摘要。清单缺少 `commit` 时回退用上一次成功构建的 `COMMIT_HASH`(CI 通过 `AGC_UPDATE_PREVIOUS_COMMIT` 传入)作为锚点,因此首次启用摘要或更换渠道后也能立即产出摘要。锚点仍不可得(清单读取失败或没有 CI 锚点)时降级为「最近客户端改动」列表并注明可能与上一版重复 —— 摘要属于附注,任何情况下都不允许因为它让发布失败。
|
||||
- 清单里的 `commit` 是非标准字段:更新插件忽略未知字段,发布脚本用它定位下一次摘要的起点。
|
||||
- 上传:安装包与 `.sig` 上传到 `agc/<channel>/<version>/`,清单以 `--force` 覆盖上传到 `agc/<channel>/latest.json`,保证 latest 指针与清单内 URL 指向已存在的对象。
|
||||
- 上传:安装包与 `.sig` 上传到 `agc/<channel>-win|mac/<version>/`,清单以 `--force` 覆盖上传到对应分区的 `latest.json`,保证 latest 指针与清单内 URL 指向已存在的对象。
|
||||
- 首装发布:发布脚本生成 `downloads`,Windows 复用已选 NSIS `.exe`,Mac 选择本次版本和目标架构匹配的非空 `.dmg`;缺失、歧义或版本/架构不匹配时失败,不发布带悬空地址的清单。上传顺序为更新包、签名及首装包全部成功后再更新渠道清单,Windows 相同对象只上传一次。`dry-run` 不写 OSS。各渠道独立写自己的清单,由 BFF 汇总,Windows 与 Mac 发布不会覆盖彼此的下载项;Mac 跨架构合并仍遵循现有单架构发布约束。
|
||||
- Jenkins 流水线需要新增渠道参数与签名凭据;签名私钥与密码只以受保护凭据注入当前进程,不写入 workspace、日志或归档产物。
|
||||
- 归档证据:安装包、`.sig`、渠道清单与源码 commit。
|
||||
|
||||
## 验收标准与证据
|
||||
|
||||
渠道与系统分离的定向验证覆盖发布脚本、上传计划、网站配置贯通及跨渠道链接拒绝:`build-release.test.mjs`、`release-oss.test.mjs`、`platform-oss client_downloads` 和 `api-server client_download`。本地隔离数据库的 API smoke 验证 `/healthz` 成功、配置 release 时仅读取 release 分区、查询参数不能覆盖渠道、未发布版本返回空列表且 `no-store`;这不代表已构建或上传 release 安装包。真实 Windows/macOS 安装、签名和更新仍由发布验收单独执行。
|
||||
|
||||
官网下载与渠道服务地址已于 `2026-09-19` 完成源码验收:
|
||||
|
||||
| 条款 | 验收方式 | 结果 |
|
||||
| --- | --- | --- |
|
||||
| 渠道服务地址、旧凭据来源隔离与登录恢复 | HTTP/API/存储定向测试、登录会话界面测试、AGC 类型检查 | 通过;无 origin token 不发送,渠道 origin 与会话回写保持一致 |
|
||||
| 动态链接、失败重试、取消与重开竞态 | 下载组件与站点壳定向 Vitest、Web 类型检查 | 通过;桌面与移动首页均挂载入口 |
|
||||
| 已发布平台自动出现与各平台独立版本 | 多平台聚合测试、组件测试及桌面/320px 浏览器受控清单 | 通过;未发布隐藏,重新打开后展示 Windows、Apple Silicon、Intel 的对应版本与链接,部分失败仍可下载有效项 |
|
||||
| 首装元数据、DMG 选择与上传顺序 | 发布脚本 39 项临时夹具测试 | 通过;限定当版/当架构唯一非空 DMG,更新包/签名/首装包先于 latest,失败不更新指针,dry-run 不执行上传 |
|
||||
| 清单传输与下载地址边界 | platform-oss 12 项与 api-server 3 项 `client_downloads` 定向 Cargo 测试 | 通过;含双渠道并行、部分失败、残缺 Windows 清单、响应体超时、大小上限、重定向与非法来源 |
|
||||
| 官网同源完整链路 | 标准本地后端 `/healthz`、匿名 `/api/client-downloads`、Vite 同源代理与真实浏览器 | 健康检查 200,下载接口 200/no-store;真实列表保留 Windows 0.1.73、隐藏未发布 Mac;桌面和窄屏可操作 |
|
||||
| 发布对象可读取 | 公开清单、安装包 HEAD 与 Range | 当日 Windows 0.1.73 清单 200,安装包 104867522 字节、Range 206 且为 EXE;macOS 清单 404,不展示下载 |
|
||||
|
||||
本次未构建或安装新客户端包,未上传或部署官网;Mac 浏览器证据使用受控清单,不能替代 Mac 原生构建、签名与安装验证。真实安装升级及生产部署后的 smoke 属于发布验收。
|
||||
|
||||
已获得的证据:
|
||||
|
||||
| 条款 | 验收方式 | 证据 |
|
||||
| ---------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 渠道与端点映射、渠道校验 | `node --test apps/ai-game-creator-shell/scripts/build-release.test.mjs` | 通过(默认渠道、错配失败关闭、未知渠道失败关闭) |
|
||||
| macOS 单架构清单与 universal 拒绝 | 定向发布脚本测试 | 单架构各用对应平台键;拒绝未闭合的 universal 发布 |
|
||||
| macOS universal 清单与双架构依赖 | 定向发布脚本与安装包验证 | 两个平台键同 URL/签名;两套资源独立校验,真机验收另记 |
|
||||
| 缺签名时失败关闭 | 同上 | 通过 |
|
||||
| 开发态不检查更新 | `vitest run apps/ai-game-creator-shell/tests/appUpdate.test.ts` | 通过(开关关闭时不请求清单) |
|
||||
| 旧自研链路整条删除 | 代码检索无残留命令、事件与白名单条目 | 通过(`download_agc_update` / 下载事件 / 清单常量均无残留) |
|
||||
@@ -128,11 +168,14 @@
|
||||
|
||||
已决策:
|
||||
|
||||
- macOS 采用单架构包,只登记实际构建架构;Intel 真机构建与跨架构清单合并未验收,不公开宣称双架构分发就绪。
|
||||
- macOS 采用 universal 包,两个平台键对应同一更新产物;主程序用 lipo 检查两种架构,Codex 资源分别做原生身份/摘要与启动验证。Rosetta 结果不代替 Intel 真机验收。
|
||||
- 旧客户端迁移桥:保留一个版本周期。渠道清单上线后,发布管线同时把旧的 `agc/latest.json`(sha256 格式)指向 `dev-win` 最新安装包,让已发布客户端自动升级到新协议;下个周期整条删除。
|
||||
- 签名密钥:由本仓库维护者生成并保管,私钥保存在仓库外(`%USERPROFILE%\.tauri\genarrative-agc-updater.key`),只有公钥进入客户端配置;Jenkins 用受保护凭据 `AgcUpdaterSigningKey` 与 `AgcUpdaterSigningKeyPassword` 注入为 Tauri 打包器读取的 `TAURI_SIGNING_PRIVATE_KEY` 与 `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`,本机可用 `TAURI_SIGNING_PRIVATE_KEY_PATH` 指向同一私钥。当前密钥不带密码;首次发布前仍可重新生成,首次发布后不可更换。
|
||||
- macOS 发布方式:`dev-mac` 产物在本机 mac 上执行发布入口上传,Jenkins 暂不新增 macOS 节点;macOS 代码签名与公证凭据未确认前,相关闭环记为未验证项,不静默通过。
|
||||
- macOS 发布方式:已接入专用 macOS Jenkins 节点(label `genarrative-agc-macos`,EXCLUSIVE 单 executor),由 `Jenkinsfile.ai-game-creator-shell-macos-build` 执行 `scripts/build-macos-ci.mjs` 完成 universal 构建、双架构隔离 smoke、universal DMG、分区清单生成、更新包验签与 OSS 上传。`AGC_RELEASE_DRY_RUN` 默认为关(与 Windows 渠道对称,即直接发布),只有勾选后才退化为「只打印上传计划、不写 OSS」的演练。
|
||||
- macOS 代码签名与公证暂缺:产物为未签名 + 未公证,构建入口剥离 `APPLE_*` 凭据跳过 Apple 签名,不传 `--no-sign`(它还会跳过 updater 的 minisign 签名,产物将没有 `.sig`);构建清单实测记录 `appleSigned` 与签名类型,`latest.json` 侧固定记录 `notarized=false`,首装需用户在 Gatekeeper 中手动放行。该限制作为已知未验证项记录,不静默通过;「安装 → 重启接管新版本」的自动更新闭环仍需实机验收。
|
||||
- 更新包验签门禁:构建完成、上传 OSS 之前,用产物内烘焙的 `plugins.updater.pubkey` 复核 `<更新包>.sig`(Tauri 使用 minisign 的 `ED` 预哈希模式)。keyId 不一致或校验失败立即失败关闭,禁止上传——客户端校验失败会直接拒绝安装,且公钥发布后不可更换。
|
||||
|
||||
待办:
|
||||
|
||||
- macOS `dev-mac` 渠道落地(macOS 构建机、签名与公证、安装后重启验证、是否接入 Jenkins macOS 节点)暂缓,由后续独立变更单独完成;在此之前 `dev-mac` 渠道只有构建与清单能力,不发布。
|
||||
- macOS 分区(`<channel>-mac`)已落地构建与发布能力:Mac Jenkins 节点、release 入口(更新包 + 签名 + 首装包 + 分区清单)、验签门禁与调度接入均已就绪,默认直接发布。
|
||||
- 剩余待办:Apple 代码签名与公证凭据(未就绪期间以未验证项记录)、macOS 安装后重启接管新版本的实机验证、Intel 真机 smoke(当前 x86_64 侧为 Rosetta)。
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# AGC 总版本号与发号
|
||||
|
||||
更新时间:`2026-09-20`
|
||||
|
||||
## 背景
|
||||
|
||||
AGC 客户端此前按「渠道各自比高水位自增」发号:`dev-win` 与 `dev-mac` 各自读自己的渠道清单,`prepareReleaseVersion()` 取本地版本与远端高水位的较大值再 `patch + 1`。两个渠道因此天然拿到不同的号,统一构建无法保证 Windows 与 macOS 是同一个版本,手工填号或并发发号还会出现重号与回退。
|
||||
|
||||
本方案把客户端版本号收敛成单一发号源,渠道只写自己本次拿到的号。
|
||||
|
||||
## 版本源
|
||||
|
||||
- 唯一事实源是 OSS 对象 `agc/global-version.json`,字段:`version`、`updatedAt`、`channel`、`commit`、`buildId`。
|
||||
- 渠道清单(`agc/<channel>/latest.json`)仍写各自本次的版本号,但不再承担发号职责。
|
||||
- 仓库里由构建改写的 5 个版本文件(`apps/ai-game-creator-shell/package.json`、根 `package-lock.json`、`src-tauri/tauri.conf.json`、`src-tauri/Cargo.toml`、`src-tauri/Cargo.lock`)继续按现状由构建改写,**只作构建输入参考、不作为事实源**,不提交、不参与发号。
|
||||
|
||||
## 发号规则
|
||||
|
||||
- `next = 总号 + 1`;总号不存在时先一次性播种,见下节。
|
||||
- 顺序固定为「先写总号 → 再构建 → 再发渠道清单」;任何一步失败都不回滚,只烧号。
|
||||
- 统一构建:发一次号,通过 `AGC_RELEASE_VERSION` 同时传给 `dev-win`、`dev-mac`(以及后续渠道),各渠道共用同一个号。
|
||||
- 单渠道热修:发一次号,只传给该渠道;其他渠道清单保持原值。
|
||||
- 显式传入 `AGC_RELEASE_VERSION` 的构建不再自行发号,只做高水位断言。
|
||||
|
||||
## 并发控制
|
||||
|
||||
- 发号收口到专用 Jenkins Job `Genarrative-Agc-Global-Version-Issue`(`disableConcurrentBuilds()` + 写后回读校验)。集群未安装 `lockable-resources` 插件,因此以 Job 级串行 + 写后回读兜底:写完总号后立刻回读,若远端值与本次写下不一致即失败关闭(号已烧,不重试、不回滚),由人工确认后再发。
|
||||
- 各渠道构建只接收号,不自己加;本地手工兜底路径(未传 `AGC_RELEASE_VERSION`)同样走「读总号 → +1 → 写回 → 回读校验」。
|
||||
- 不允许裸读改写:任何路径都必须经过发号模块,写后回读不一致即失败关闭。
|
||||
|
||||
## 一次性播种
|
||||
|
||||
- 基线 `seed = max(仓库当前版本, agc/dev-win/latest.json, agc/dev-mac/latest.json, agc/latest.json 旧指针)`。
|
||||
- 播种只写基线本身,不递增、不烧号;首个发放号是基线 + 1。
|
||||
- 播种入口:`node apps/ai-game-creator-shell/scripts/issue-global-version.mjs --seed-only`,或发号 Job 勾选 `SEED_ONLY`。
|
||||
|
||||
## 实现位置
|
||||
|
||||
- 发号模块:`apps/ai-game-creator-shell/scripts/agc-global-version.mjs`(读总号 / 播种 / 发号 / 写后回读 / 渠道高水位断言 / dry-run 预览)。
|
||||
- 发号入口:`apps/ai-game-creator-shell/scripts/issue-global-version.mjs`(CI 与本地共用,输出固定为 `AGC_GLOBAL_VERSION=<version>`)。
|
||||
- 构建侧:`build-release.mjs` 的 `prepareReleaseVersion()` 优先采用传入总号,未传入时现场发号;`resolveRemoteHighWaterVersion()` 降级为断言来源,只用于「请求号低于本渠道清单版本即失败关闭」。
|
||||
- CI:`jenkins/Jenkinsfile.agc-global-version-issue` + `jenkins/agc-global-version-issue-job-config.xml`;调度管线 `jenkins/Jenkinsfile.scheduled-revision-trigger` 与手动管线先调发号 Job,再把号透传给 AGC Build。
|
||||
- 发号 Job 的 Copy Artifact 采用生产权限模式,`options` 里的 `copyArtifactPermission(...)` 必须显式列出**全部**消费者:定时调度 `Genarrative-Scheduled-Revision-Trigger` 与手动发布 `Genarrative-Manual-Build-And-Deploy`。用户触发的构建按「认证用户」判权(SYSTEM 定时构建短路放行),漏列时手动发布会报 `Unable to find project for artifact copy: Genarrative-Agc-Global-Version-Issue`,且失败点在发号之后。改完该 `options` 后必须先单独跑一次发号 Job,让 Declarative Pipeline 把 Job property 写回 Jenkins。
|
||||
|
||||
## 不改的东西
|
||||
|
||||
- `appUpdate.ts` 与官方 updater 的比较逻辑、渠道清单端点。
|
||||
- `release-oss.mjs` 上传流程。
|
||||
- `agc/latest.json` 只由 `dev-win` 写入。
|
||||
- 主站下载检查的读取源,不接总号。
|
||||
|
||||
## 边界约定
|
||||
|
||||
- `dev-mac` 在 macOS 构建机本地发布时同样只接收号(发号 Job 或本地发号脚本),不允许手动填号。
|
||||
- `AGC_RELEASE_DRY_RUN` 只读总号并预览 +1,不写回、不烧号。
|
||||
|
||||
## 验收标准
|
||||
|
||||
1. 统一构建后,`dev-win` 与 `dev-mac` 清单版本相同且等于总号。
|
||||
2. 单渠道热修后,只有该渠道清单变化,总号 +1,其他渠道清单原值不动。
|
||||
3. 热修后再统一构建,总号继续 +1,其他渠道版本跳过中间号。
|
||||
4. 并发两次发号不出现重号。
|
||||
5. 传入低于本渠道当前版本的号时构建失败关闭。
|
||||
@@ -8,6 +8,17 @@ AGC 客户端接入公共 OSS 上的**游戏模板库**(真·游戏模板,
|
||||
- 客户端侧:Rust `template_library` 模块(读清单、下载、安装、建项目)+ 模板库全屏页 + 首页模板推荐 + 左侧导航入口。
|
||||
- 不在本次范围:模板制作工具、模板审核、模板计费、增量更新、已建项目的模板回填。
|
||||
|
||||
## 模板库灰度访问
|
||||
|
||||
- 后台现有灰度发布页登记 `agc:template-library`,控制整个模板库;复用用户 ID 白名单、用户标签、拒绝名单和稳定用户分桶比例,不新增数据库表或字段。未配置此 Gate 时默认关闭;已配置且停用灰度时遵循现有语义全量开放,启用灰度时拒绝名单优先于白名单及比例。
|
||||
- `/api/runtime/frontend-config` 增加 `agcTemplateLibraryEnabled`,按认证主体返回服务端权威结论。客户端尚未得到结果、匿名或请求失败时关闭入口;权限结果不作为离线缓存。
|
||||
- 登录、主体变化、窗口重新获得焦点和每次模板操作时刷新权限,不承诺后台修改的实时推送。未获准时首页模板推荐和左侧模板库导航隐藏,不读取 OSS 清单;已在模板页检测到失去权限时回到首页。原生命令明确拒绝或权限检查失败后同样清空并关闭入口。账号切换或退出后立即清空前一主体的清单、操作状态和可见性,旧异步响应不能恢复权限或导航。
|
||||
- 原生清单读取、下载和模板建项命令均独立核对当前平台会话及服务端权限,不能依靠前端隐藏;远端等待后的会话变化必须拒绝,安装和建项写入使用当前会话身份保护。已缓存清单和已安装模板不能绕过权限。
|
||||
- 此灰度控制当前客户端的产品功能,不承诺公开 OSS 模板内容的保密性,也不限制已创建项目的正常打开和编辑。旧客户端升级后才接入此控制。
|
||||
- 验收覆盖缺省关闭、明确全量、允许/拒绝名单、标签和比例、请求失败、缓存绕过、原生命令阻断、首页/导航/模板页以及账号切换竞态。
|
||||
- 验证入口:后台灰度页面测试、`useTemplateLibrary.test.tsx`、AppSurface 实际挂载的 template 用例、原生 `template_library` 测试和后端 `frontend_runtime_config` 测试。退出开始使用既有平台会话代次立即撤销,建项返回、revision 读取和预览核验后的旧回调均不得导航或登记最近项目;同主体 token 轮换不误撤销原生身份。
|
||||
- 本地隔离数据库已验证 Gate 经后台 API 保存后可重新读回,匿名运行时配置为 false;后台受控浏览器 smoke 验证桌面和 320px 布局及保存确认交互。线上 OSS 下载和正式安装包登录后的端到端操作不由这些测试替代。
|
||||
|
||||
## OSS 契约
|
||||
|
||||
```text
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
> 规范关系:AGC 插件与编辑器适配主规范
|
||||
> 验收范围:插件 manifest、宿主生命周期、RPC、Capability/权限审计、UI 挂载和编辑器适配器边界
|
||||
|
||||
更新时间:`2026-09-18`
|
||||
更新时间:`2026-09-20`
|
||||
|
||||
## 目标与边界
|
||||
|
||||
@@ -20,6 +20,7 @@ packages/agc-plugin-sdk/src/index.ts
|
||||
server-rs/crates/editor-adapter-api/src/lib.rs
|
||||
plugins/agc-cocos-editor/ (第一个编辑器插件包)
|
||||
plugins/agc-unity-editor/ (Unity Mono 编辑器插件包)
|
||||
plugins/agc-godot-editor/ (Godot GDExtension 编辑器插件包)
|
||||
```
|
||||
|
||||
现有 DirectProject 的 Skill/MCP 导入仍保留。它们是 Codex 扩展注入链路,不等同于本宿主管理的可运行 AGC Plugin。
|
||||
@@ -72,7 +73,16 @@ OpenAI 的标准模型是“Plugin 作为可安装包,组合 Skills、可选 M
|
||||
|
||||
Windows 与 macOS 构建都将内置插件的清单、JS 入口与面板复制到应用资源目录;staging 每次重建,避免已删除插件或跨目标原生 payload 残留。macOS 不携带 Windows native payload。Cocos 进程桥接仍仅按既有 Windows 平台实现提供,插件文件可被发现不代表 macOS 已支持编辑器控制;JS 入口的系统 Node 前提不变。
|
||||
|
||||
Cocos 插件对用户可见与可启动必须同时满足当前为 Cocos 项目、宿主已注册 `cocos-editor` 原生适配器;没有适配器时从插件/扩展列表隐藏,直接启动或读取面板也在产生子进程前拒绝。正式适配器仅在 Windows 且编译 `cocos-editor-execute` 时注册;Agent 工具使用相同平台与 feature 门禁。前端只按后端列表投影判断是否自动启动,不自行推断平台能力。
|
||||
Cocos 与 Unity 插件的可见性、启动、面板、插件 RPC 和 Agent 工具暴露不按当前工程类型过滤;无当前项目以及普通 AGC、Godot、Cocos、Unity 项目使用同一套插件可用性规则。宿主必须已注册插件声明的原生适配器;没有适配器时从插件/扩展列表隐藏,直接启动或读取面板也在产生子进程前拒绝。正式适配器与 Agent 工具继续受各自平台和编译 feature 约束,禁用开关继续阻止启动与工具执行。前端只按后端列表投影判断是否自动启动,不自行推断平台能力或再次按工程类型过滤。
|
||||
|
||||
### 工程上下文与真实编辑器目标
|
||||
|
||||
- 工程类型只用于工程识别及对应工程工作流,不作为 `agc-cocos-editor` / `agc-unity-editor` 的管理、面板或工具目录门禁。Runtime、DirectProject MCP、工具策略快照和模型上下文使用一致规则;工具已暴露不代表真实编辑器已连接或操作已成功。
|
||||
- 前端对宿主列表中的 Cocos、Unity 与 Godot 插件分别根据适配器支持、启用、Runtime 入口和运行状态投影自动启动,不按项目类型二选一;自动启动不自动展开编辑器面板。Godot 是否属于当前项目由宿主过滤,Cocos/Unity 保持工程类型独立性。
|
||||
- 项目切换不因新工程类型不同而停止插件或隐藏工具;当前受控项目上下文仍须按既有顺序更新,旧编辑器连接失效。旧请求与回执保留原项目归属,不能更新新项目连接状态。
|
||||
- 实际编辑器操作仍须取得有效的当前受控项目和与之匹配的真实编辑器目标。宿主注入项目路径,显式路径必须与当前受控项目一致;适配器继续校验真实工程结构、目标 PID、进程身份、版本与握手。无项目、不匹配的工程目录、无编辑器或不支持的平台均应在发送编辑器操作前明确失败,不回退到其它项目或任意编辑器进程。
|
||||
- 插件启用状态、manifest 适配器绑定、`editor.rpc` 权限、超时与并发拒绝、执行结果不确定阻断均保持原合同。取消工程类型过滤不增加自动重试,不清除项目切换或重启插件前已经产生的不确定状态。
|
||||
- 不新增或迁移项目类型、内置插件开关、API/DTO、SpacetimeDB schema 或持久项目数据;不扩大 Cocos/Unity 原生适配器的平台支持,也不把任意非引擎目录解释为可执行的编辑器工程。
|
||||
|
||||
### 内置插件与可用开关
|
||||
|
||||
@@ -123,8 +133,29 @@ Unity 插件复用此扩展点,GUI 适配器通过已有 Runner RPC 转发到
|
||||
Windows x64 的 Attach helper 来源、构建工具链和执行回执合同见
|
||||
[Unity 插件接入](./【技术方案】AGC Unity编辑器插件接入-2026-09-18.md)。
|
||||
|
||||
Godot 使用同一 Runner 执行与回执确认层,按引擎分别保存 pending/uncertain 状态,不能相互确认或清除。`godot-editor` 的受控连接允许按用户已选方案维护项目内 `.gdextension` 引用及 Godot 自动生成的 UID;DLL 随安装资源分发,探测仍只读,原始项目配置和场景不改。具体文件归属、GDScript 错误/async、升级卸载与分发合同见 [Godot 插件接入](<./【技术方案】AGC Godot编辑器插件接入-2026-09-20.md>)。
|
||||
|
||||
## 编辑器常用操作指导
|
||||
|
||||
Unity 与 Godot 的常用操作指导由客户端既有审核 Skill pack 随包提供;每个引擎有独立入口和常用操作参考,不新增执行工具或任意文件读取入口。DirectProject 在隔离 Skill 目录发现指导,也可通过已有 `agc_read_skill_resource` 按审核名称和相对路径读取。Agent Runtime 的对应执行工具说明包含同一份常用操作参考,避免只覆盖 Codex 原生 Skill 路径。正文只有一个源码来源,清单指纹与安装投影必须一致。
|
||||
|
||||
指导覆盖场景/层级查询、对象或节点的创建/修改/删除、组件或属性、Prefab/PackedScene、资源引用、基础 UI、打开/保存场景、运行/停止及诊断。示例是提交给现有 execute 的代码正文,说明前置状态、预期回读和保存/撤销语义,不写宿主路径、PID、令牌或桥接安装动作。读说明不连接编辑器、不授权修改;执行仍受原插件开关、平台、项目和权限门禁控制。
|
||||
|
||||
每个引擎的操作参考不超过 14 KiB UTF-8,并独立包含执行参数和失败边界;Direct 常驻提示只给读取路由,16K 字符预算内必须保留两个引擎的指南入口。Runtime 的最终工具定义须完整包含对应参考,不能因截断丢失末尾内容,也不能在该执行工具不可用时额外注入正文。
|
||||
|
||||
通用 CapabilityRegistry 保留原有短描述和 4000 字符约束;完整参考只在已注册能力转换为 Provider 函数工具时附加,按 UTF-8/LF 规范化并校验 14 KiB 上限。不扩大 core 的任务、能力或摘要长度合同。
|
||||
|
||||
执行载荷仅有 `code`;Direct 工具的顶层参数为 `{code}`,Runtime 原生函数沿用 `{reason,input:{code}}` 外层,指南必须按实际工具 schema 区分这两种调用格式。
|
||||
|
||||
确定失败也可能已经产生部分修改;未知结果继续禁止自动重放。Unity 使用实际场景与对象身份,区分 Undo、Prefab override、保存和 Domain Reload。Godot 使用真实编辑场景根、为需保存的新节点设置 owner,区分独立 UndoRedo 回滚与编辑器历史,避免承诺未验证的 Ctrl+Z。主线程同步死循环不可硬中止。
|
||||
|
||||
验收要求:两个入口均能取得审核正文,源码与安装后的字节一致;非法路径及未登记资源继续拒绝;系统提示能够发现指南但不塞入全部示例;从指南原文提取代码做真实临时工程验证,至少覆盖查询、修改与回读、局部撤销、场景持久化、资源实例化和 UI。未实测操作和平台须在交付记录中明确,不把 API 示例当作已有独立工具。
|
||||
|
||||
当前证据覆盖本地指南读取/安装、最终工具描述、真实编辑器执行示例及 Godot 示例图形渲染。Unity GUI 视觉和真实 Provider 读取指南后调用编辑器的端到端链路尚未验收,不能由定向测试或前置状态检查推断通过。
|
||||
|
||||
## Tauri 命令
|
||||
|
||||
|
||||
`list_agc_extensions` 返回统一的 Plugin/Skill/MCP catalog;`list_agc_plugins`、`refresh_agc_plugins`、`start_agc_plugin`、`stop_agc_plugin`、`reload_agc_plugin`、`call_agc_plugin` 和 `read_agc_plugin_panel` 提供 Runtime Plugin 管理入口;`set_agc_plugin_project_path` 设置当前项目的受控上下文。编辑器适配器通过宿主 registry 和 Plugin RPC 使用,不增加编辑器专属 Tauri 命令。
|
||||
|
||||
编辑器操作统一走 `host.rpc`:插件用 `extensions.world.genarrative.agc.adapter` 或显式 `adapter` 参数选择适配器,宿主校验 `editor.rpc` 权限后调用 `EditorAdapter::rpc`。项目上下文通过 `host.events.subscribe` 的响应和 `project.changed` 事件 payload 下发,插件不需要自己扫描目录。
|
||||
@@ -141,6 +172,7 @@ OpenAI 官方 Plugins 文档将 Skills、MCP Server 和可选 UI 定义为同一
|
||||
- Rust:manifest 路径/权限校验、目录扫描、权限拒绝和通用适配器 registry 边界单测;`cargo check --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml`。
|
||||
- 前端:`agc-plugin-sdk` TypeScript 编译、宿主服务类型检查,以及 `PluginPanelHost` 的挂载/卸载测试。
|
||||
- 内置插件开关:`builtin_plugins` 单测覆盖默认值、持久化往返、坏文件失败关闭,以及“禁用后工具目录里不再出现该工具”;`plugin_host` 单测覆盖禁用后不能启动、启用后回到 stopped。
|
||||
- Cocos/Unity 工程类型独立性:覆盖无当前项目、普通 AGC、Godot、Cocos、Unity 上下文中的插件列表、启动、面板、RPC 与 Runtime/DirectProject 工具目录一致性;项目切换不因类型变化停止这两个插件。Godot 单独验证当前工程门禁、切项目撤销旧上下文与停止旧实例。保留禁用开关、缺失原生适配器、平台/feature、显式跨项目路径拒绝与真实编辑器目标校验的独立反例;非引擎目录不能仅因工具可见就通过实际操作校验。
|
||||
- 插件工作区:`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml` 覆盖工作区扫描与 manifest 启用状态;`cargo test --manifest-path plugins/agc-cocos-editor/native/cocos-editor-bridge/Cargo.toml` 覆盖 Cocos 适配器;`node --test plugins/agc-cocos-editor/src/entry.test.mjs` 覆盖插件入口协议与 manifest 一致性。
|
||||
- 通用仓库门禁:`npm run check:encoding`、`git diff --check`;发布前仍需单独执行 AGC package smoke 和安装包 smoke。
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ AI Game Creator Shell 采用 IDEA 风格的当前进程错误报告:错误事
|
||||
|
||||
## 客户端
|
||||
|
||||
- 诊断 URL 保留可定位的 API 路由路径,隐藏 origin、URL 账号密码、查询参数、fragment 和路径中的敏感标识;普通资源 URL 与本地文件路径继续隐藏。网络错误、HTTP 错误与响应体超时均应带安全路由,不能只剩 `<url>`。历史已经脱敏的归档不推测或补造原路由。
|
||||
|
||||
- 捕获 React render error、`window.onerror`、`unhandledrejection` 以及显式标记的 Tauri/API/Agent 错误。Agent Runtime 的终态失败、预算耗尽和启动确认失败由 Rust 失败投影统一入池;Direct Codex 与专业 Agent 的前台裸 Tauri invoke catch 作为补充入口,重复事件由同一 fingerprint 合并,主动取消和“同一 turn 已在运行”不作为错误采集。
|
||||
- 事件字段包括 eventId、fingerprint、source、message、stack、时间和次数;重复事件合并。不再携带 severity、errorCode、page、action、requestId 等无法稳定关联的字段。
|
||||
- 指纹计算可使用调用方的 page/action 及脱敏后的首个调用点作为进程内区分输入,但这些上下文不会作为事件字段上传;消息与 stack 在入池前统一脱敏,WebCrypto 失败时降级为稳定可读指纹,采集本身不得产生新的未处理拒绝。
|
||||
@@ -28,6 +30,8 @@ AI Game Creator Shell 采用 IDEA 风格的当前进程错误报告:错误事
|
||||
- 后台接口:`GET/PATCH /admin/api/error-reports/{batchId}`、`GET /admin/api/error-reports` 和受保护的 `/download`。列表支持 `limit`/`offset` 分页并返回 `total`、`hasMore`;OSS 读取先检查 `Content-Length` 并在流式累计超过上限时立即中止,不把超限对象完整缓存在内存中。
|
||||
- 这些是 api-server 内部登录/管理员路由,不属于 `/api/external/v1`,不纳入 External OpenAPI;管理员详情对不存在返回 404,对归档/元数据损坏返回 500。
|
||||
- admin viewer 仅接受 error-reports Tab 权限,支持列表筛选、分页、详情、状态 `new/in-progress/resolved`、处理备注和受控下载;不存在的更新目标返回 404,存储损坏返回 500。列表行支持键盘 Enter/Space 打开详情,详情事件预览最多显示 20 条,完整内容通过诊断包下载获取。
|
||||
- 列表提交时间与详情事件时间接受 RFC3339、`seconds.microsZ`、Unix 秒/毫秒/微秒字符串;非法或越界时间显示 `-`,不显示 `Invalid Date`。仅调整呈现,不改变数据库字段或历史归档。
|
||||
- 详情按后台现有弹窗、按钮与表单样式分层呈现报告摘要、事件消息、可展开调用栈、用户说明、日志附件和处理备注;支持 Esc、焦点管理、窄屏内部滚动。加载/下载/保存失败在当前可见面板内提示;关闭或打开另一报告后,旧请求不得覆盖新报告。状态与备注保存以后端响应为准。
|
||||
- 管理员列表、筛选、状态和备注全部读取/更新 SpacetimeDB;错误报告的 list/get/update procedure 要求 `require_editor_generation_runtime_service_identity`,详情先读表再从 OSS 下载并解析 ZIP,下载接口直接从 OSS 返回 ZIP。PATCH 更新直接返回元数据,不重新下载归档;详情弹窗提供备注编辑器。无需新增管理员 DELETE HTTP 接口。每日清理任务按分页扫描全部报告,删除过期 OSS 对象后再删 DB;任一 DB 删除失败会保留错误并在后续周期重试。详情解析对解压后的 `events.jsonl` 设置 24 MiB(与请求体上限一致)与 100 条事件上限。管理员备注超过 2,000 字会被明确拒绝;module 同时校验固定 OSS key、SHA-256、归档大小和事件/日志计数上限;OSS 读取仅将明确不存在映射为 404,其余错误按请求无效或上游故障返回。内部 OSS 读写只允许 `agc/error-reports/v1/`。旧本地报告不迁移。
|
||||
|
||||
SpacetimeDB `error_report` 表字段:`batch_id` 主键、`user_id`、`submission_id`、`idempotency_key` 唯一键、`object_key`、`archive_sha256`、`archive_size_bytes`、`event_count`、`log_count`、首个 fingerprint/source、`review_status`、`admin_note`、`created_at`、`updated_at`;索引为 `(user_id, submission_id)`、`created_at`、`review_status`。
|
||||
|
||||
@@ -1,5 +1,151 @@
|
||||
# AI 游戏创作智能体 App 实施计划
|
||||
|
||||
## 2026-09-20 DirectProject 七项效率闭环(补齐合同)
|
||||
|
||||
本节补齐并覆盖下节中仅靠 Skill 要求预检、收尾、批读和原生命令预算的部分。完整目标仍为:自动预检、宿主验收与收尾、分层验证、统一执行/返修预算、稳定测试基线、请求耗时与批量读取、所有工具并行。已有代码及测试不等于全部目标已完成;按下表逐项验收。
|
||||
|
||||
| 要求 | 必须成立的行为 | 完成证据 |
|
||||
| --- | --- | --- |
|
||||
| 自动预检 | 新建 Web 游戏由实际用户入口和宿主自动执行,不依赖模型主动调用;失败不得启动正式生成或付费素材 | 首页/后端正反用例、真实构建与双端截图 |
|
||||
| 验收与收尾 | 必需范围和验收项在宿主持久化;证据绑定当前输入;全部必需项通过后关闭本轮新的修改/执行/付费扩项并产生交付报告 | 真实工具链状态转移、重启/并发/新增扩项拒绝用例 |
|
||||
| 分层验证 | 视觉、定点玩法、项目测试和必要完整闭环按已登记标准执行;不混淆证明范围 | 双端正例与单端失败反例 |
|
||||
| 统一预算 | 内置验证、托管脚本和原生命令执行受同一宿主预算约束;不以命令文本猜测“是不是试玩”,不把每条普通开发命令单独计为一次返修 | 捆绑 app-server 的执行前控制、拒绝无副作用、跨入口/重启/耗尽/超时用例 |
|
||||
| 稳定基线 | 可复用的固定种子跑酷基线,真实短按/长按跳跃、单次收力、滑铲释放及公平越障窗口 | 物理单测、双端真实输入、原案例缺陷参数反例 |
|
||||
| 速度可归因 | 已实现请求分段计时继续有效;新增实际并行批读与宿主首轮上下文预取 | 有界读取/并发屏障/安全边界/减少独立读取往返证据 |
|
||||
| 工具并行 | 现有全部工具并行、在途上限、同资源事务和付费防重继续有效 | 混合调用、图片双 POST 同时到达、同参防重回归 |
|
||||
|
||||
### 自动预检与可信脚手架
|
||||
|
||||
- 首页只有结构化的“做游戏 + 直接构建”入口执行预检;先于自动命名和项目生成。策划、文档、素材与已有项目普通对话不按文字关键词触发。后端再按 creationType 与实际工程类型核验,Godot/Unity/Unreal/Cocos 不强制 Web 工具链。
|
||||
- 全局预检使用独立临时目录和已校验 Node/npm,执行自带的最小构建脚本、生成 dist,再用正式受限浏览器完成 desktop/mobile 页面与 PNG 检查。此步骤无网络、无平台生成,不运行用户脚本,不修改已有项目。
|
||||
- 对客户端刚创建且内容仍匹配可信模板的 Web 脚手架,在正式生成前由宿主执行受控依赖准备和真实 Vite 构建。依赖安装禁用生命周期脚本;不自动安装或覆盖导入/用户修改过的工程。
|
||||
- 准备凭证的 `ready` 只证明初次环境准备成功,不代表当前游戏已验收,后续正常修改不得因此重新安装。`preparing` 中断恢复必须证明原拥有者已结束且其执行子树已回收;身份或归属未知时保留阻断,不重复执行。
|
||||
- 输出明确区分“Node/npm 构建能力通过”与“本项目 Vite 构建通过”;任何一步失败保留可行动错误,不降级为预检成功。
|
||||
- 安装载荷提供相同的安全预检 CLI,验证无系统 Node 的独立运行、缺包/篡改失败关闭。NSIS 解包载荷 smoke 与真实安装器注册流程分开报告,不覆盖当前用户的既有安装。
|
||||
|
||||
### 跑酷固定基线
|
||||
|
||||
- 新增固定场景 runner-v1。客户端使用固定种子 20260920,通过 URL 初始化参数提供;游戏从初始化状态读取并投影实际种子。模拟随机数与装饰随机数分离,模拟采用固定步长。
|
||||
- 在现有只读玩法状态接口中扩展 runner 状态:实际模拟 tick、课程指纹、角色位置/速度/落地、滑铲、碰撞尺寸、跳跃/收力计数、真实按住状态和最近障碍几何。字段缺失或类型不符即不支持该验收,不补造成功。
|
||||
- desktop 使用真实键盘/鼠标,mobile 使用真实触摸;检查短按和长按、松手单次收力、落地、滑铲释放与同种子重开。不得调用游戏内部动作方法、改状态、替换 Math.random 或直接设置胜利。
|
||||
- 公平窗口以观测到的可越障空中区间减去碰撞区横穿时间计算,基线至少 180ms;窄窗口参数作为负例并报告实际观测值。固定场景只证明短时动作与几何,不冒充原案例数值的精确复现或完整长关卡通关。
|
||||
- 配套可复用、可单测的物理/种子模块;不把所有新游戏都强制改成跑酷,不替换用户选定引擎。
|
||||
|
||||
### 有界并行读取
|
||||
|
||||
- 新增批量项目上下文读取:最多 8 个去重文件,最多 4 个同时读取;单文件实际读取最多 1MiB,单项正文最多 32KiB,整个序列化回包最多 256KiB。
|
||||
- 复用项目路径、权限、敏感内容和私有控制面边界,拒绝越界/链接/非普通文件;先有界读取,再按 UTF-8 行裁切。返回行号、实际内容摘要、截断和下一页位置。
|
||||
- 单项失败不丢弃其余成功项;返回安全项目身份、回合身份和读取前后 revision。检测到文件或回合变化必须标 stale,不能把它宣传为原子快照。
|
||||
- 宿主在正式 Direct 首轮批量预取受支持的基础源码/包信息,作为明确的数据上下文交给模型;不把文件内容提权成系统指令。大型或不支持文件明确留给后续批读,不循环逐文件请求模型。
|
||||
|
||||
### 宿主控制与后续验收边界
|
||||
|
||||
- 统一预算按执行/验证批次与实际运行输入管理,允许正常构建和相关测试在一个批次内执行;原生读取和结构化文件编辑不按每条命令消耗返修次数。任意代码执行必须具备当前回合的有效执行许可及累计执行时长边界。
|
||||
- 原生命令入口必须以捆绑版本的真实协议证明可在执行前拒绝;不得用执行后的日志通知或文本分类器冒充执行门。能力检测失败不得静默退回无控制模式。
|
||||
- 原生执行控制的精确协议与许可持久化,在对应里程碑评审后落地;不得提前宣布这一项已完成。
|
||||
- 不修改 Provider 的 maxRetries;不减少引擎或任意代码执行的合法能力,不引入平行 Agent 框架,不改公开 API/数据库,不提交私密运行记录。
|
||||
|
||||
### 宿主验收与执行许可合同
|
||||
|
||||
- 正式 GUI 和 CLI 的共同 Direct 回合入口建立宿主控制状态,绑定 canonical 项目路径、稳定 clientTurnId 和原始用户输入摘要;宿主私有目录保存权威账本并独占该回合,项目 `.agent` 仅允许保存展示副本。配置或项目侧文件被改写、工具切换、Provider 重试和进程重启不得刷新同一回合的预算。
|
||||
- 普通聊天与读取不要求交付合同。首次修改、代码执行或付费扩项之前,模型通过结构化工具登记本轮必需范围与验收项;合同非空、有界且只冻结一次。模型只能声明要求,不能提交“通过”作为证据。后续扩项留到新的用户回合。
|
||||
- 明确新 Web 创建由宿主可信脚手架凭证及尚未交付的宿主记录判定,CLI 同样据此判定,不从提示文本猜测;这种回合即使模型没有调用工具或没有登记合同,也不得按普通聊天宣布交付。已有项目只有未激活合同且从未产生副作用时才允许直接聊天结束。
|
||||
- 验收项为明确类型的产物、构建/测试命令、双端视觉或指定固定场景的双端玩法。可信新 Web 游戏由宿主补充构建、双端视觉和玩法底线,不能由模型声明“已有项目”降低。已有项目按冻结的变更范围选择层级;平台美术只在用户目标要求时成为必需项。
|
||||
- 同一份双端玩法证据可同时满足视觉项,避免重复浏览器运行。构建证据分别绑定源码输入摘要与输出摘要,正常生成 dist 不算源码漂移;浏览器证据绑定构建后的实际运行输入。只有宿主验证完成产生的结构化结果和证据文件摘要能满足合同,项目内自行写出的验证 JSON 无效。
|
||||
- 产物项的初始摘要由宿主冻结,模型不能提供或在重放时重算。仅登记已经存在的文件不能立即交付:产物必须实际变化/新出现,或有当前指纹的宿主可信验证证据;原生修改和工具修改遵守相同判据。
|
||||
- `validation.maxRuns` 保留已配置值,语义为执行/返修批次上限;首次执行开启第一批。开发期正常成功命令和源码编辑共享本批,不逐条消耗次数。开始验证后绑定输入,执行失败或验证期间输入漂移使本批进入排空状态,关闭新的执行入口,等已受理操作结束后才开启下一返修批次。读取与结构化编辑不单独消耗次数。
|
||||
- `validation.maxExecutionSeconds` 默认 900,必须为正整数,是整个 clientTurnId 的累计执行时间上限,换批次不清零。并行操作分别计时累加,内置工具不与 app-server 的外层 MCP 事件重复计费。时间耗尽立即拒绝新执行、写入和付费扩项,保留最近证据与未完成项;Provider 的重试次数保持独立。
|
||||
- `validation.maxTurnSeconds` 默认 1800,必须为正整数,是同一宿主回合从开始起的墙钟上限,重启不重置,用于约束模型空转和超出单个工具事件边界的后台会话。墙钟上限与累计执行时间分别记录,任一耗尽都收束自有执行器;不能把模型等待时间报告成工具执行时间。
|
||||
- 捆绑 app-server 的原生命令使用已验证的逐次审批能力;宿主只返回单次接受/拒绝,不允许会话授权或 exec policy 修订。第三方 MCP 必须显式启用逐调用询问,不能依赖不可信 readOnlyHint。询问缺少调用 ID 时,按服务器与回合中的并发组保守管理,不能解析展示文案猜测归属。
|
||||
- 原生、内置与第三方所有入口都经过同一宿主状态;独立工具继续并行,只有身份、批次切换、收尾与必要资源冲突形成短临界区。能力检测失败不得退回无控制执行。
|
||||
- 上述回合控制适用于 DirectProject。独立客户端 HTTP MCP 显式使用 ExternalClient 来源,保持其原有权限、幂等和浏览器能力,不借用当前项目另一条 Direct 回合的预算或可信证据;新交付合同与托管验证命令工具要求 Direct 会话。服务端 external_mcp 不变。
|
||||
- 必需证据齐备后,宿主先进入封口状态,拒绝新副作用,再确认在途归零、收束模型执行器并取得进程退出证明,最后重新核对源码、产物和证据摘要;核验成功原子进入 completed 并产出宿主报告。不能先写 completed 再尝试停止后台进程。宿主主动结束模型回合属于交付终态,不触发普通错误反馈或重试。未达标不得用模型最终回复替代验收。
|
||||
- Windows app-server 在任何模型工具执行前绑定不可脱离的自有 Job,超时/取消/断连时验证整个 Job 已退出。其它平台继续保留受控进程组;未取得完整子树退出证据时按不确定状态报告,不宣称全部后台执行已停止。已受理的远端付费任务保留原不确定围栏,断连不构成自动重放授权。
|
||||
- 付费许可始终绑定原回合和原租约,不能在容量或同动作锁排队结束后借用新回合。排队可取消,每次新 POST 前与封口共用宿主状态短锁,核对原许可、期限与阶段并持久化提交边界;封口、终止或耗尽后不得新增提交。已经越过提交边界的请求不丢弃,其 operation ID 和不确定状态继续持久化并允许原 GET 对账,多阶段生成的下一次 POST 仍须重新核验。ExternalClient 与手工资源操作保持既有语义。
|
||||
- 本地写事务未结算或失败仍需核对时不得封口。与失败写入重叠的旧写入/旧验证不能清除该围栏;只有失败之后新准入的成功修复或可信验证可以恢复验收。普通文件写入与账户/本地资产导入显式携带原写入许可,取得项目锁后再与宿主状态锁共同核验期限并提交短本地事务;等待锁或下载期间终止的请求不得继续落盘,网络等待不持宿主状态锁。
|
||||
|
||||
|
||||
## 2026-09-20 DirectProject 交付效率与可观测性
|
||||
|
||||
本节是 DirectProject 新建 Web 游戏和已有游戏修改的交付合同,覆盖下文要求所有修改重复完整试玩、模型自报 attempt 作为预算、工具桥串行生成的旧表述。目标是提前发现环境问题、复用稳定验证能力、在本轮目标达标后结束,并用真实记录区分执行与等待。非目标:降低素材来源或玩法验收要求、修改 Provider 重试配置、改变原生 shell 的权限模式、自动修改系统环境或无授权付费再生。
|
||||
|
||||
### 环境与工作流
|
||||
|
||||
- 客户端交付配套 Node/npm;发布包从本机已安装且与目标平台/架构一致的工具链制作受校验资源,保留许可并校验内容摘要。安装态不依赖系统 PATH 的 Node;开发态可使用已验证的宿主运行时。不得从项目或相对 PATH 加载伪造运行时。
|
||||
- 新建 Web 游戏在生图和大量实现前执行客户端环境预检,检查 Node/npm 的实际版本、浏览器启动和 CDP 可用性。报告只包含安全状态、版本、耗时和错误码。缺失或异常必须尽早返回阻塞,不能指示模型改宿主环境、全盘搜索或自行下载一套运行时。编辑器工程不强制 Web 工具链。
|
||||
- 预检不安装依赖、不修改项目 revision、不请求平台生成;构建仍执行项目自己的 npm 脚本。Codex 隔离 HOME 与平台凭据边界保持不变,客户端把已验证的运行时加入执行 PATH,不能把宿主凭据目录交给模型。
|
||||
- 第一轮先明确本次必需玩法、素材和验收项。同批独立读取尽量合并,必需图片一次规划;已有且可用的资产复用。已有目标全部通过后给出交付结果,非阻塞的新点子列为后续工作,不在收尾时主动开启新的生产链。
|
||||
|
||||
### 分层验证与预算
|
||||
|
||||
- 复用客户端浏览器与现有固定玩法场景。视觉检查采集双端画面/布局/资源/诊断;玩法检查分别在 desktop/mobile 执行明确的固定场景和真实输入,按视口保存结果。旧报告缺少移动端玩法结果时保持未知,不补通过。报告必须声明检查层级;视觉通过不能宣称玩法通过,固定场景通过也不能宣称覆盖未执行的完整关卡。
|
||||
- 输入或碰撞改变先做定点玩法检查;纯图像/颜色变化做视觉检查;首次交付和影响闭环的修改做所需玩法验证。新增失败或相关代码变化才重跑对应层,不因改说明文字重复完整验证。
|
||||
- 内置试玩和客户端托管的外部 Node/npm 验证共用当前 clientTurnId 的持久化预算。客户端分配递增执行序号,模型提供的旧 attempt 仅作兼容输入,不能减少计数或重置预算;同一轮错误反馈、工具切换和进程重启均不能刷新已消费次数。
|
||||
- 新增本地 validation.maxRuns(默认 3,正整数)独立于 llm.maxRetries;显式配置原样使用,不按角色或运行模式改写。超限直接返回已用/上限和最近证据,停止新的验证。预检与正常构建不计作重复试玩。
|
||||
- 同一层级、场景/验证命令和项目输入指纹已有成功证据时复用,不再次运行;真实源码或素材变更使缓存失效,失败不缓存为成功。所有验证回执标明是否复用、项目指纹、实际序号和预算剩余。
|
||||
- 外部验证只通过客户端提供的 Node/npm 入口运行,在同一预算内保存退出码和有界输出;生产 Skill 明确禁止转到原生 shell 自建并重复执行另一套试玩来规避预算。任意原生 shell 的语义不能由字符串猜测可靠识别,本合同不声称已通过权限沙箱硬阻断所有绕行。
|
||||
- 鉴权、权限、余额、项目身份、传输丢失、取消及付费结果不确定继续遵守原终止/对账边界;确定性参数错误先修参数,不原样重复付费请求。
|
||||
|
||||
### 分段耗时
|
||||
|
||||
- 在既有 Direct 回合审计记录中追加计时,关联 clientTurnId、独立 attempt 和 request 身份。记录 configured/requested model、reasoning effort 与封闭的路由分类;上游返回的 model 单独标明,不能把配置值冒充实际模型。
|
||||
- 区分连接准备、客户端回合锁等待、turn/start 应答、HTTP 发出到响应头、首 body chunk、首 SSE event、首内容 delta、流终态、工具与上下文压缩。不可见的上游排队/推理保持未知,缺字段不得补零。
|
||||
- 并发时按区间并集计算占用,同时保留分维度统计,不能把重叠时间累加为整轮墙钟。条目记录达到上限后统计仍继续;流 EOF、错误、取消和 Drop 均正确收尾。
|
||||
- 只保留时间、计数、模型安全标识和状态,不保留凭据、端点 URL、请求/响应正文或推理内容;统计失败不能覆盖本来的业务结果。旧历史不回填推测值。
|
||||
|
||||
### 工具并发
|
||||
|
||||
- 所有工具调用均允许并行受理,不能按工具类型或由 STDIO 逐行 await 将独立工作整体串行化。完整 JSON 行通过单一写端输出并按请求 ID 回包;真正有依赖的调用由调用方等待前置结果。
|
||||
- DirectProject 的 Responses 代理明确发送 `parallel_tool_calls=true`,保持选定模型、推理参数与其它请求字段不变;不依赖捆绑 SDK 对未知模型的串行回退。MCP 服务使用真实支持的 `supports_parallel_tool_calls` 选项,保留工具真实读写标记及执行前许可。上游拒绝并行能力时如实返回,不自动改成串行或切换模型重发。
|
||||
- MCP 调度的在途容量为 8 并具有背压;只为同一资源的冲突操作、项目短事务、编辑器单实例或同一付费动作保留必要串行边界。不同文件/不同资源/不同独立工具可同时推进,不保留覆盖所有资源远端等待的粗粒度锁。
|
||||
- 客户端独立图片在途上限为 2,这是客户端自己的资源上限,不代表探测到了服务端配额。不得通过放松同一动作槽幂等锁、账户身份栅栏或付费不确定性围栏换并发。
|
||||
- 不同动作允许同时远端执行,manifest 只在提交阶段短持项目锁并重读合并;同一精确动作在排队和执行期间均不得重复 POST。容量释放、进程/通道关闭、同参排队和失败恢复有专门回归覆盖。
|
||||
- 不改公开 External v1、计费、SpacetimeDB schema 或平台 worker 配置。
|
||||
|
||||
### SDK 串行工具的等价接入
|
||||
|
||||
- DirectProject 对外保留完整补丁和计划能力,由宿主 MCP 提供 `agc_apply_patch` 与 `agc_update_plan`。精确关闭 SDK 的旧计划工具注册;缺少合法回包通道的原生问答工具不再声明可用,需要用户信息时使用现有聊天。
|
||||
- 通过同一可信捆绑 Codex 和身份/路由隔离的 HOME 取得完整模型目录,只置空 `apply_patch_tool_type` 以移除 SDK 全局串行补丁处理器。不得修改模型名称或其它 metadata,不为未知模型伪造显式条目;匹配和 fallback 仍由 SDK 执行。
|
||||
- 代理模式的 bundled 目录和 OAuth 的实际远端/有效缓存来源分别核验,不能把模型目录导出 exit0 当作远端成功。每个 Direct 用户回合创建新模型目录快照和进程;旧执行器完全收束后才进入下一回合,不在活动执行中重启或重放。该行为是按回合冻结 metadata,不是实例内动态 overlay。
|
||||
- OAuth 在目录捕获与模型进程内产生的轮换结果仅在宿主私有 runtime 中延续,按原始认证来源指纹、路由、项目及稳定账户/用户身份绑定后复制到下一回合的新私有 HOME;不回写用户原始认证文件,不缓存 API Key。来源、路由或身份改变时不继承,迟到的旧实例不得覆盖新回合结果;轮换结果未确认时禁止重用旧 token。
|
||||
- 实例退役与验收完成使用不同判据:Windows 仍要求完整 Job 退出;Unix 已确认主进程退出且所控进程组为空时可退役并允许下一显式用户回合,但 group-only 证明不能使原会话从 Interrupted 升为 Completed,不能自动重放旧操作。主进程、所属组或退出状态仍未知时继续阻断新实例。
|
||||
- 补丁完整复用固定版本官方语法解析与执行语义。宿主枚举每个源和目标(包括所有 Move 和重复操作),检查项目边界、受保护路径和链接,在本地短写事务中复核后执行;取消、预算、退出证明和未知结果仍走统一宿主控制。失败可能已有部分修改,不能声称全批回滚或自动原样重放。
|
||||
- 模型计划进度保存到同一回合的宿主状态,仅作展示,不等于验收通过;计划更新和长资源调用可以同时推进。真正共享资源的修改仍保持必要顺序。
|
||||
|
||||
### 验收
|
||||
|
||||
| 条款 | 必需证据 |
|
||||
| --- | --- |
|
||||
| 环境可用 | 运行时分发/完整性定向测试,真实 Node/npm 与浏览器 CDP smoke,缺失与损坏失败关闭 |
|
||||
| 验证收敛 | 同轮跨入口与重启预算测试,超限停止,成功复用与源码/素材变更失效,层级不混淆 |
|
||||
| 交付收尾 | 内置 Skill/提示词与工具合同一致,定向回归,无旁路无限试玩指引 |
|
||||
| 可观测 | 本地 mock SSE 分片/错误/Drop/跨轮测试、区间并集测试、模型身份与敏感数据边界 |
|
||||
| 工具并发 | 不同工具可在首个响应前开始且乱序按 ID 回包;两个不同图片同时到达 mock 平台,同参只提交一次,容量和 manifest 合并测试 |
|
||||
| 整体 | 范围匹配 Rust/脚本测试、类型检查、Skill 包校验、文档索引、编码与 diff 检查;真实 Provider/安装包未运行时单独列明 |
|
||||
|
||||
|
||||
## 项目主模型使用记录
|
||||
|
||||
- 项目在 `.agent/model-usage.jsonl` 保存主模型记录,供复制或压缩完整项目后查询,不新增界面展示。每条只保存版本、记录时间、来源、请求模型标识、可确认的模型名称,以及可用的客户端回合和线程身份;不保存提示词、响应正文、连接地址或凭据。
|
||||
- DirectProject 每次提交回合前保存本次请求的模型快照,来源为 `turn-request`。官方目录标识与真实型号分开:官方标识只写 `requestedModel`,不能把 `platform-default`、目录 ID、`Direct Codex` 或 `codex-app-server` 当成模型名;自定义路由保存明确提交的型号。响应返回的模型名以 `provider-response` 追加,使用请求发出时冻结的项目/回合归属,后续切模型不会覆盖历史。
|
||||
- 模型名从成功 Responses JSON 的顶层 `model` 或 SSE `response.created` / `response.in_progress` / `response.completed` 中的 `response.model` 提取;只读取白名单字段,有界处理分块和异常数据,响应内容仍原样转发。上游没有返回型号时保留请求证据,不推测实际模型。
|
||||
- 旧项目通过 `project.status` 读取 manifest 成功后补录一次:在有界范围内优先恢复项目内可明确识别的主模型运行记录,超出历史扫描预算时跳过对应候选;没有找到可信历史型号时写入当前配置快照并标注 `current-config-backfill`,不声称其为历史事实。官方配置只有目录标识时保留标识,模型名为空;以后真实回合仍继续追加。补录幂等,不覆盖或改写原项目历史。
|
||||
- 记录复用项目受控路径与追加锁;无写权限、损坏或超限时保留已有文件并写安全诊断,不因此中断项目打开、模型响应或触发额外付费重试。响应记录在阻塞工作线程中追加,文件锁等待不阻塞响应原始块转发。补录仅发生在项目 `project.status` 命令和本次回合入口,不扫描机器上的其它项目或私人会话。
|
||||
- 验收覆盖:请求与响应的模型差异、跨回合/项目隔离、切模型保留历史、旧项目补录及幂等、分块 SSE/JSON、不含模型/失败响应不伪造型号,以及凭据/正文不落盘。使用本地 HTTP fixture 验证代理透传和落盘,真实供应商调用另行报告。
|
||||
|
||||
| 合同 | 自动化证据入口 |
|
||||
| --- | --- |
|
||||
| 来源区分、切模型保留历史、补录幂等、损坏/超限保护 | `project::model_usage::tests` |
|
||||
| JSON/SSE 字节透传、模型落盘与跨回合归属 | `agent::codex_provider_proxy::tests::model_usage_proxy_*` |
|
||||
| 分块 UTF-8、换行、多行事件和有界解析 | `agent::codex_provider_proxy::model_usage::tests` |
|
||||
| 文件锁争用不阻塞响应、流中断仍保留已确认型号 | Provider 代理的锁争用与流错误 fixture |
|
||||
|
||||
记录格式版本为 `schemaVersion: 1`。`recordedAtMs` 是记录时间,`historicalModelConfirmed` 仅在响应观测或可信历史恢复时为 `true`;它在请求快照和当前配置补录时为 `false`。上述本地 fixture 不替代真实供应商或安装包验收。
|
||||
|
||||
## 2026-09-20 Godot 编辑器插件
|
||||
|
||||
已有 Godot 工程通过内置 `agc-godot-editor` 接入 `godot.editor.execute` / `agc_godot_execute`,复用通用 PluginHost、EditorAdapter、Runner、可用开关和权限审计。DLL 随 AGC 安装资源分发,项目内受管描述文件触发官方 GDExtension 聚焦加载;工作区与实际 Godot 根继续遵守双根合同。GDScript 的真实完成、编译/运行错误、await 和不确定回执按 [Godot 编辑器插件接入](<./【技术方案】AGC Godot编辑器插件接入-2026-09-20.md>) 验收,不能用原型或模拟回执替代正式实机结果。
|
||||
|
||||
## 2026-09-17 GameCreationApp 资源 kind:唯一词汇表、严格解析与 `app_log!` 留痕
|
||||
|
||||
本节覆盖 2026-09-15 节里关于「canonical 字符串列表 / legacy 别名表 / `tracing` 留痕 / ts-rs 生成路径」的表述;枚举成员集合、「不迁移、不静默转换」的总体口径不变。
|
||||
@@ -69,6 +215,18 @@ Rust 侧在 `server-rs/crates/shared-contracts` 维护唯一权威 `GameCreation
|
||||
- 当前素材名以现有正式命名链路为准:生成时 assetName 参与落盘名称,重命名更新文件名;卡片消费正式资源 label,不从临时输入或历史任务名覆盖后续重命名,不新增平行显示名持久化。若原有命名链路丢失 assetName,则修复原链路,而非只在卡片本地伪造。文档卡不显示任何正文摘要,但详情原文与 JSON 识别读取不变。
|
||||
- 验收覆盖空素材项目进入工具、真实引用入参、成功/失败/重试与迟到响应、占位移动后落点、全类型名称、文档详情、当前栏目重排/撤销、不同缩放的多选移动/撤销以及其他栏目不变。自动化、真实客户端和真实 Provider 验证分别报告;未实际运行的路径不得标为通过。
|
||||
|
||||
## 平台服务与官网分发
|
||||
|
||||
客户端正式包的平台服务跟随构建渠道:release 连接正式服务,dev 连接开发服务;本地 debug 登录页保留 release/dev/custom 服务器选择。凭据按 origin 隔离;官网下载入口汇总最新渠道清单,自动展示已有首装包的 Windows/Mac 平台及架构。完整合同见 [AGC 客户端更新检查与下载](./【技术方案】AGC客户端更新检查与下载-2026-08-31.md) 的“官网下载与客户端服务地址”。
|
||||
|
||||
## Agent 提示词与回合测试边界
|
||||
|
||||
预览快捷操作的 UI 回归按独立命令及必要的连续操作拆分,每例使用新的项目 fixture、invoke mock 和页面,项目打开后再采样副作用调用计数。保留命令输出、草稿填入、焦点、权限确认和原生参数断言;预览启停、快照回滚取消、导出确认与取消作为各自短流程验证,使用默认测试超时。定向运行 `npx vitest run apps/ai-game-creator-shell/tests/appSurface.test.ts -t "预览快捷操作:"`。
|
||||
|
||||
提示词拼装测试核对动态错误、外置片段和实际 Provider 请求结构,避免绑定可自由润色的中文原句。源码载荷限制由实际动作校验与 Provider 修复测试覆盖;工具参数自动修复和超预算上下文压缩继续验证恢复结果、状态及私有信息隔离。Direct 消息原样转发复用生产请求转换测试,文件变化与版本幂等直接测试生产文件投影函数,删除测试专用的重复回合编排。
|
||||
|
||||
Rust 分片日志在失败时输出有界 stdout 尾部中的失败段,保留 panic 位置、断言详情和 Rust 汇总;selected 只表示该分片选中的用例数量。成功分片保留简短摘要。定向验证使用相关 Rust 用例与 `node --test apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.test.mjs`。
|
||||
|
||||
## 策划 Agent 批量局部修改
|
||||
|
||||
`patch_file` 的所有 edits 均匹配同一份原文件,参数顺序不影响结果。完成唯一匹配与不重叠校验后,按原文起点升序拼接未修改片段与替换文本,最后一次性写入;任一校验失败时不写文件。回归用例覆盖乱序 edits、中文内容与替换长度增减,并核对完整落盘内容。此行为仅属于策划 Agent 文件工具。
|
||||
@@ -419,13 +577,13 @@ Supervisor 认领该回执后,由父 run 自己为每个原 delivery 逐一创
|
||||
|
||||
- 模式合同:客户端 AppData 配置新增全局 `agentMode`,只接受 `codex_cli / provider`。缺省和新安装默认使用 `codex_cli`,原有 HTTP LLM Provider 路径完整保留并可显式切回 `provider`;切换只影响下一次节点请求,不新增 Runner、任务图、会话库、配置库或业务事实源。
|
||||
- 调度边界:正式 DAG、manifest、Agent task/session/run 身份、队列、锁、委派、all-join、完成门、Provider lifecycle、持久 retry/handoff 与 `needs-reconciliation` 继续由现有 AGC Runtime 掌控。每个被调度节点在 `codex_cli` 模式下直接启动一次非交互 `codex exec` 充当该节点的推理 Agent;Codex 返回当前 Runtime 广告函数的结构化调用,Runtime 仍是唯一 ToolHost,不允许 CLI 自己写项目、执行命令、调用 MCP 或形成第二套 revision / verification 真相。
|
||||
- 安装包侧车:Windows x64 release 固定随 Tauri resource 打包 `@openai/codex@0.147.0` 的原生 `codex.exe`;Rust build script 从 AGC 子包锁定依赖 stage 到 resource,并写入版本与 SHA-256 清单。Windows 侧车映射只写入 `tauri.windows.conf.json`,通用 `tauri.conf.json` 不得让 Linux / macOS 构建依赖未生成的 Windows 二进制。运行时只在文件摘要和 `codex-cli` 版本同时匹配清单时优先选内置侧车;缺失、损坏或版本漂移时跳过它,按既有 npm 安装、PATH 顺序回退。安装包同时携带 Apache-2.0 第三方声明;API Key、`auth.json`、Cookie、Token、用户 `CODEX_HOME`、用户配置和项目数据绝不打包。
|
||||
- 安装包侧车:Windows x64 release 固定随 Tauri resource 打包 `@openai/codex@0.155.1` 的原生 `codex.exe`;Rust build script 从 AGC 子包锁定依赖 stage 到 resource,并写入版本与 SHA-256 清单。固定版本只在 `build_support/codex_bundle.rs` 声明一次,构建脚本、宿主补丁执行器身份、逐次审批协议允许列表和模型目录捕获共同引用它,避免多处字面量漂移。Windows 侧车映射只写入 `tauri.windows.conf.json`,通用 `tauri.conf.json` 不得让 Linux / macOS 构建依赖未生成的 Windows 二进制。运行时只在文件摘要和 `codex-cli` 版本同时匹配清单时优先选内置侧车;缺失、损坏或版本漂移时跳过它,按既有 npm 安装、PATH 顺序回退。安装包同时携带 Apache-2.0 第三方声明;API Key、`auth.json`、Cookie、Token、用户 `CODEX_HOME`、用户配置和项目数据绝不打包。
|
||||
- Windows x64 release 安装包只生成 NSIS,不生成 MSI:`tauri.windows.conf.json` 的 `bundle.targets` 固定为 `["nsis"]`,通用配置继续保留其它平台的默认打包目标。安装后的产品名、开始菜单 / 桌面快捷方式和 EXE 产品描述统一由 `tauri.conf.json` 的 `productName: "陶泥儿"` 生成;应用 identifier 与内部可执行文件名保持稳定。内置 Codex 资源安装到顶层 `coding-agent/win-x64/`,运行时从同一路径查找 `bin/codex.exe` 与 `manifest.json`;仓库 staging 仍使用 `resources/codex/win-x64/`,包内子目录、组件名、版本和完整性校验保持原合同。
|
||||
- macOS 单架构安装包同样必须携带锁定版本的原生 Codex、`codex-code-mode-host`、`rg`、上游 zsh、`codex-package.json` 和第三方声明,保留上游相对布局;构建时按 Cargo 目标选择 npm 原生依赖,缺文件、版本或目标不匹配立即失败,不借用开发机 PATH 里的 Codex。资源只在 `tauri.macos.conf.json` 映射到 `Contents/Resources/coding-agent/mac-native/`。构建与运行共享平台文件白名单,运行时由当前 `.app/Contents/MacOS` 定位相邻 `Resources`,完整性与版本验证通过后优先使用内置组件;失败沿既有外部安装回退,不能运行未校验的内置文件。单架构资源不能冒充 universal 包。
|
||||
- macOS 安装包必须携带锁定版本的原生 Codex、`codex-code-mode-host`、`rg`、上游 zsh、`codex-package.json` 和第三方声明,保留上游相对布局;构建时按 Cargo 目标选择 npm 原生依赖,缺文件、版本或目标不匹配立即失败,不借用开发机 PATH 里的 Codex。资源只在 `tauri.macos.conf.json` 映射到 `Contents/Resources/coding-agent/mac-native/darwin-arm64/` 与 `darwin-x64/`。构建与运行共享平台文件白名单,运行时由当前 `.app/Contents/MacOS` 定位相邻 `Resources`,完整性与版本验证通过后优先使用内置组件;失败沿既有外部安装回退,不能运行未校验的内置文件。universal 主程序同时携带两套独立原生资源,运行切片按 Cargo 目标只选择对应目录;macOS 构建必须预备两套锁定原生依赖,不读取全局 Codex。
|
||||
- 内置插件的清单、运行入口与面板同时在 Windows/macOS 随包分发,继续由既有 PluginHost 的应用资源目录扫描入口发现;不携带开发依赖、缓存、测试或私有配置。插件文件随包不等于原生适配器跨平台:Cocos 进程桥接仍受现有 Windows 实现和 feature 门禁约束,macOS 原生桥接另行设计与验收,不复制 Windows DLL 冒充支持。系统 Node、用户 Cocos Creator、账号登录、网络和生成工程的 npm 工具链仍是现有外部前提,不在此次 Codex 侧车补齐中隐式变更。
|
||||
- macOS 安装包验收必须包括:脱离仓库位置的 `.app` 资源与架构检查、受限 PATH/隔离 HOME 下内置 Codex 启动和 app-server 握手、必需文件缺失/篡改/平台错误的拒绝测试,以及 DMG 完整性检查。真实登录、Provider 对话、GUI 和 Cocos 操作必须独立列出证据,不能用压缩包生成或 `--version` 成功替代。未配置正式签名、公证的本地测试包不得作为公开发行包。
|
||||
- macOS 安装包的系统下限取主程序和全部原生组件中的最高要求;锁定 Codex 0.147.0 原生依赖所携带的 zsh 要求 macOS 15.0,因此 `bundle.macOS.minimumSystemVersion` 明确为 `15.0`。更新原生依赖时重新检查 Mach-O 的系统下限,不能只按 AGC 主程序宣称兼容版本。
|
||||
- 发布链路的目标解析和单架构清单以《AGC客户端更新检查与下载》为准:CLI 目标优先,版本、构建、端点、bundle 与更新清单共用单一发布上下文。插件能力以《AGC通用插件宿主与编辑器适配》为准:无已注册 Cocos 原生适配器时隐藏且拒绝启动,前端自动启动只消费后端可用性投影。
|
||||
- macOS 安装包的系统下限取主程序和全部原生组件中的最高要求;锁定 Codex 0.155.1 原生依赖中 `codex-resources/zsh/bin/zsh` 的 `LC_BUILD_VERSION` 下限为 macOS 15.0(`bin/codex`、`codex-code-mode-host`、`codex-path/rg` 分别为 11.0 / 10.12),因此 `bundle.macOS.minimumSystemVersion` 明确为 `15.0`。更新原生依赖时重新检查 Mach-O 的系统下限,不能只按 AGC 主程序宣称兼容版本。0.155.1 的 macOS 原生包新增 `codex-resources/voice/`(语音宿主与 GStreamer 动态库);AGC 不启用语音能力,侧车清单只 stage 上述组件,不打包该目录,未来如启用语音需重新评估依赖与许可。
|
||||
- 发布链路的目标解析和 universal 清单以《AGC客户端更新检查与下载》为准:CLI 目标优先,版本、构建、端点、bundle 与更新清单共用单一发布上下文。插件能力以《AGC通用插件宿主与编辑器适配》为准:无已注册 Cocos 原生适配器时隐藏且拒绝启动,前端自动启动只消费后端可用性投影。
|
||||
- CLI 安全边界:CLI 固定使用 argv 启动,禁止 shell 拼接;工作目录使用本次请求专用的空临时目录,不把游戏项目绝对路径写入 prompt、stdout、stderr 或持久记录。调用固定使用 ephemeral、忽略用户配置和 exec rules、read-only sandbox、never approval,并关闭 Codex shell tool;只继承 CLI 运行和认证所需的最小环境,显式移除宿主 `CODEX_API_KEY`。用户级 Codex 登录态继续由本机 Codex 自己读取,API Key、auth 文件、Cookie、Token、`CODEX_HOME` 私有内容不得复制到项目配置、Runtime sidecar、Agent DB、conversation 或日志;stdout / stderr 无换行时也受硬上限约束,stderr 诊断只记录固定分类、字节数和 SHA-256。
|
||||
- 协议边界:Runtime 把既有 `LlmRunRequest` 的消息和当前函数目录编码为有界 prompt,并从同一函数 JSON Schema 生成 Codex structured-output schema。CLI 输出转换为现有 `LlmRunResponse / LlmToolCall` 后,继续经过 native tool / MCP 参数校验、动作上限、权限、pending、receipt、验证与格式修复链;最终回复仍走唯一提交路径,不新增平行响应协议。
|
||||
- 取消与恢复:Codex 子进程绑定当前 Provider request lifecycle,取消、暂停、Runner draining 或 GUI owner 丢失时终止并回收当前进程;started 后没有可信终态仍沿现有 Provider reconciliation 处理。`agentMode`、CLI 可执行身份和影响输出的 Codex 参数进入 `providerConfigFingerprint`,模式切换不得消费另一模式遗留的 retry/handoff。
|
||||
@@ -464,6 +622,8 @@ V1.11 的受保护仓库控制目录同时包含 `.git / .agent / .agents / .cod
|
||||
|
||||
Prompt 静态门禁必须断言上述 Bundle section 当前定义的权威语义与组合关系;身份文案调整后应同步更新旧断言,不得继续依赖已经退出 Bundle 的历史连续措辞,也不得在测试或 Provider builder 中复制一份平行 Prompt。
|
||||
|
||||
Agent 可见的系统指令、工具与参数说明、恢复指引和上下文模板统一由外置提示词文件维护。AGC 沿用 `prompts/runtime/manifest.json`:已有 composition/section 保持原有组合关系,独立调用的文本按职责登记在 `textCatalogs`,目录为 `prompts/runtime/texts/`,每份 JSON 是稳定文本键到正文的映射。构建期校验目录、文件、重复键和空正文,并生成可供 `format!` 使用的编译期文本宏;变量填充沿用 Rust 格式语法。Runtime 状态、用户内容、schema 类型与枚举、权限和校验继续由代码生成。服务端 Agent 的独立 crate 使用各自 `prompts/` 中的编译期文本文件。迁移以当前组装结果和工具 schema 等价为验收依据,源码门禁检查各提示词入口的内联正文与外置引用。
|
||||
|
||||
2026-07-12 起,通用开发能力的 Runtime V1.1 增量以 [`【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`](<./【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md>) 为编码级事实源。它补充仓库启动上下文、同一发布二进制独立 Runner、受限本地预览浏览器验证、动态隔离子 Agent 和真实 Provider 全链路验收;本文件中“进程内 tokio task”“首轮不预加载项目内容”和“不创建动态执行实例”的旧口径由 V1.1 明确替代,未涉及能力继续沿用本文件。
|
||||
|
||||
同一文档的“V1.2 对标 Codex CLI 增量”继续作为受控命令与推理档位的事实源。对一次性 `command.exec` 而言,只接受 Runtime 白名单内的固定 `program` 和逐项 `args` argv,默认 `confirm`,可执行文件解析为项目外绝对路径且子进程只使用安全 PATH;不解析 shell 字符串,不提供管道、重定向、PTY 或后台进程。这里对 PTY 和后台进程的排除仅适用于 `command.exec`,不能用来否定 V1.10 的独立持久进程工具,也不能把 `command.exec` 自身改成长驻入口。`command.exec` 的 action、stdout / stderr、退出码、超时与源码指纹结果统一进入现有 `action / observation`、project revision、verification gate 和 `needs-reconciliation` 链路;只有明确验证型命令且退出码、源码指纹、命令日志、manifest 与 Agent DB 审计全通过才签发 passed gate,Git / rg / cargo metadata / 普通 npm run 只作诊断。首版只请求终止受控进程组,安全等级与 `project.verify` 相同,不宣称已具备完整 OS sandbox 或 detached-process 隔离。
|
||||
@@ -1367,12 +1527,12 @@ game-project/
|
||||
|
||||
## 2026-08-20 Direct Codex 审核 Skill Pack 与受控工具内核
|
||||
|
||||
- 普通项目对话只由一个 project-bound Codex app-server thread 执行。客户端系统提示词只放最小工程合同、当前游戏源码有界快照、项目 prompts 和审核 Skill 索引;不再批量读取项目 `.codex/.agents` Skill 正文,也不恢复 Supervisor、专业 Agent 或 harness。
|
||||
- 首页恢复“做游戏 / 做素材 / 做方案”三个创作类型,默认“做游戏”。该选择与设置页的 Agent Runtime 模式无关;每次首页提交仍只自动创建一个新项目并进入项目工作台。用户正文原样进入项目对话,`game|art|doc` 仅作为受限结构化首轮上下文传给同一 Codex thread,不拼接“初始意图”文案、不产生首页对话、不切换 Provider 或恢复旧 Runtime 编排。
|
||||
- `agc-skill-pack.v1` 只包含项目结构、陶泥儿美术、Web 游戏实现、真实浏览器试玩、客户端资源投影五项 Skill。清单记录用途、触发条件、所需工具、版本和内容 SHA-256;审核文本按 UTF-8 读取并将 CRLF 规范为 LF 后计算指纹和安装,避免混合换行造成 Windows / Linux 构建结果漂移,语义内容变化时必须同步重算对应清单指纹并提升版本。同步统一运行 `npm run agc:skill-pack:sync`,只读校验由 AGC `typecheck` 和 release build 自动执行,发现漂移时直接列出 Skill 与实际摘要,不让失配内容进入构建产物。客户端把审核文件安装到隔离目录后通过 app-server `skills/extraRoots/set + skills/list` 注册并复核,完整正文由 Codex 原生 Skill 机制按意图加载,一层引用只能经 `agc_read_skill_resource` 读取清单内 Markdown。引用路径按平台无关规则拒绝反斜杠、盘符、UNC、绝对路径和 `..`,不能依赖当前宿主的 `std::path` 语义判断其它平台路径。
|
||||
- DirectProject 始终连接客户端内置的 `agc_tools` STDIO MCP;2026-08-31 起还会在启动时接入客户端扩展仓库中用户已启用的独立第三方 STDIO/HTTP MCP 配置,但不读取用户全局 Codex MCP、不开启完整 Plugin Runtime。内置工具固定为审核引用读取、标准陶泥儿美术准备、已登记资源有界查询、视频 / 角色动画 / 音效 / BGM 的 create-or-derive 语义生成、已登记图片去背景、desktop/mobile 浏览器试玩和受控 `agc_web_search`。内置 MCP 进程只做协议;真实浏览器、付费 External v1 调用与受控搜索通过随机 loopback 地址回到客户端主进程,因此不复制 GUI 登录态、开发者 Key、项目路径、revision、operation 或幂等键到模型上下文。内置与用户启用的第三方 MCP 工具都沿用 DirectProject 自动批准方式,但付费资源工具仍由客户端绑定稳定回合身份、限制单回合请求数、串行执行并优先恢复匹配账本;通用 shell、Codex 原生 webSearch、任意原生命令网络、多 Agent 和完整插件能力继续关闭。`llm.webSearchEnabled` 只控制 DirectProject 的 AGC 受控搜索工具暴露与执行,Codex 原生 `web_search` 始终保持 disabled;Provider、ToolHost、DirectHome 不纳入本次联网主链路。
|
||||
- 陶泥儿生成继续复用持久幂等账本、operation 恢复、来源/下载/PNG 解码和 manifest 登记;普通客户端优先使用当前 AGC 登录会话及账号路由,只有受控的 ExternalDeveloper 发布模式才在客户端内部使用按服务器 origin 隔离的私有 Key。用户和模型都不需要提供或配置 API Key;凭据失效、来源不明或结果未知时失败关闭,不能自动换 Key 或重新扣费。
|
||||
- 自定义 LLM API Key 路由只在 DirectHome/DirectProject 经 loopback `/responses` 流式代理转发。代理不注入 Key,只要求请求自带 Bearer,并剥离开发网关错误携带的 `X-Codex-*` ChatGPT 账户额度头,防止隔离 app-server 把 API Provider 误判为余额 0;旧 ToolHost 保持原 Provider 行为。
|
||||
- 普通项目对话由一个 project-bound Codex app-server thread 执行。客户端系统提示词包含最小工程合同、项目 prompts 和审核 Skill 索引;源码与 Skill 正文按任务需要读取。提示词、工具描述与 Skill 直接描述当前任务、输入和成功条件,细节按调用需要提供。
|
||||
- 首页提供“做游戏 / 做素材 / 做方案”三个创作类型,默认“做游戏”。每次首页提交自动创建一个新项目并进入项目工作台。用户正文原样进入项目对话,`game|art|doc` 作为受限结构化首轮上下文传给同一 Codex thread。
|
||||
- `agc-skill-pack.v1` 包含完整游戏交付流程、项目结构、陶泥儿美术、Web 游戏实现、真实浏览器试玩、客户端资源投影,以及 Unity/Godot 编辑器常用操作八项审核 Skill。清单记录用途、触发条件、所需工具、版本和内容 SHA-256;审核文本按 UTF-8 读取并将 CRLF 规范为 LF 后计算指纹和安装,避免混合换行造成 Windows / Linux 构建结果漂移,语义内容变化时必须同步重算对应清单指纹并提升版本。同步统一运行 `npm run agc:skill-pack:sync`,只读校验由 AGC `typecheck` 和 release build 自动执行,发现漂移时直接列出 Skill 与实际摘要,不让失配内容进入构建产物。客户端把审核文件安装到隔离目录后通过 app-server `skills/extraRoots/set + skills/list` 注册并复核,完整正文由 Codex 原生 Skill 机制按意图加载,一层引用只能经 `agc_read_skill_resource` 读取清单内 Markdown。引用路径按平台无关规则拒绝反斜杠、盘符、UNC、绝对路径和 `..`,不能依赖当前宿主的 `std::path` 语义判断其它平台路径。
|
||||
- DirectProject 连接客户端内置的 `agc_tools` STDIO MCP,并在启动时接入客户端扩展仓库中用户已启用的独立第三方 STDIO/HTTP MCP 配置。内置工具包括审核引用读取、图片生成、标准陶泥儿美术准备、已登记资源有界查询、视频 / 角色动画 / 音效 / BGM 的 create-or-derive 语义生成、已登记图片去背景、desktop/mobile 浏览器试玩和受控 `agc_web_search`。内置 MCP 进程负责协议;真实浏览器、付费平台调用与受控搜索通过随机 loopback 地址回到客户端主进程,GUI 登录态、开发者 Key、项目路径、revision、operation 与幂等键由客户端持有并隔离于模型上下文。内置与用户启用的第三方 MCP 工具沿用 DirectProject 自动批准方式;付费资源工具由客户端绑定稳定回合身份、串行执行并优先恢复匹配账本。`llm.webSearchEnabled` 控制 DirectProject 的 AGC 受控搜索工具暴露与执行。原生工具与审批权限以下方“DirectProject Codex 完整访问覆盖”为准。
|
||||
- 陶泥儿生成复用持久幂等账本、operation 恢复、来源/下载/PNG 解码和 manifest 登记;普通客户端使用当前 AGC 登录会话及账号路由,受控的 ExternalDeveloper 发布模式在客户端内部使用按服务器 origin 隔离的私有 Key。凭据失效、来源不明或结果未知时失败关闭,不能自动换 Key 或重新扣费。
|
||||
- 自定义 LLM API Key 路由在 DirectHome/DirectProject 经 loopback `/responses` 流式代理转发。代理使用请求自带的 Bearer,并剥离开发网关错误携带的 `X-Codex-*` ChatGPT 账户额度头,按实际 API Provider 响应判断请求结果。
|
||||
|
||||
- 2026-08-12 计划拒绝恢复:结构化 `runtime.plan_update` 被 Runtime 拒绝后,下一轮 Provider 请求按请求级目录收窄到实际项目 mutation 与 `respond_to_user`(已进入协作编排的 Supervisor 保留 `agent.delegate / agent.run_status`),并明确禁止再次规划、读取、搜索或验证;后续已有真实 mutation observation 后解除临时目录,不改变持久 executable policy。
|
||||
|
||||
@@ -1433,13 +1593,13 @@ game-project/
|
||||
|
||||
## DirectProject 工具权限现行覆盖(2026-08-24)
|
||||
|
||||
本文早期关于“DirectProject 关闭通用 shell、原生网络和主动工具”的描述属于迁移前基线;2026-09-14 起,DirectProject 的 Codex sandbox 与审批规则由下方“完整访问覆盖”取代。其余 ToolHost/DirectHome 合同不变。客户端审核的 `agc_tools` MCP 继续承担平台美术、资源登记、去背景、浏览器试玩和受控搜索,并保留项目锁、幂等账本、下载校验、恢复与投影权威。
|
||||
DirectProject 的 Codex sandbox 与审批规则以下方“完整访问覆盖”为准。客户端审核的 `agc_tools` MCP 承担平台美术、资源登记、去背景、浏览器试玩和受控搜索,并持有项目锁、幂等账本、下载校验、恢复与投影权威。
|
||||
|
||||
## DirectProject Codex 完整访问覆盖(2026-09-14)
|
||||
|
||||
DirectProject 现明确采用 Codex app-server 的 `danger-full-access` sandbox:thread 使用 `sandbox="danger-full-access"`,turn 使用 `sandboxPolicy.type="dangerFullAccess"`,不再发送 `workspaceWrite`、`writableRoots` 或项目根文件批准白名单。DirectProject 收到 app-server 的文件变更、命令执行和权限请求时直接接受,Codex 原生能力不再按项目路径做二次白名单裁剪;用户选择的项目目录仍作为 cwd 和 AGC 业务身份根,用于连接池、审计与客户端受控 MCP 的项目绑定。
|
||||
DirectProject 采用 Codex app-server 的 `danger-full-access` sandbox:thread 使用 `sandbox="danger-full-access"`,turn 使用 `sandboxPolicy.type="dangerFullAccess"`。DirectProject 收到 app-server 的文件变更、命令执行和权限请求时直接接受;用户选择的项目目录作为 cwd 和 AGC 业务身份根,用于连接池、审计与客户端受控 MCP 的项目绑定。
|
||||
|
||||
这项覆盖只改变 Codex 原生 app-server 的 sandbox 与审批边界:首页只读对话、AGC `agc_tools` MCP 的业务授权、Provider 凭据隔离、Runtime 审计与客户端 `agc_write_file` 的产品契约继续有效。系统提示词不再把 `.agent/`、`.git/`、项目外路径等描述为 Codex 原生能力禁区,但仍要求不要把 Token、Cookie、auth.json、`.env` 或 Runtime 私有控制面主动输出到对话、工具参数和日志。
|
||||
首页按只读对话执行;AGC `agc_tools` MCP 按业务授权执行,Provider 凭据保持隔离,Runtime 审计与客户端 `agc_write_file` 遵守各自产品契约。Token、Cookie、auth.json、`.env` 或 Runtime 私有控制面禁止主动输出到对话、工具参数和日志。
|
||||
|
||||
DirectProject 使用 `approvalPolicy=never`,避免每次原生调用再经过泛化 ToolHost 包装;原生命令网络随完整 sandbox 开放;联网资料仍可走受控 `agc_web_search`。多 Agent、Apps、完整插件 Runtime、hooks、Goals、Workspace Dependencies、Tool Suggestion 和原生浏览器/电脑控制仍关闭,避免绕过 AGC durable delegation、浏览器证据和副作用审计;图片生成通过客户端审核的 `agc_tools.agc_generate_image` 暴露普通单图、角色图、视觉规范图和 UI 设计图,完整游戏美术包继续使用 `agc_tools.taonier_prepare_game_art`,两者都复用同一客户端登录态、幂等账本、下载校验和 manifest/revision 投影,不开放 Codex 原生 image tool。app-server 使用隔离 `CODEX_HOME`:内置 `agc_tools` 由客户端启动参数注入,用户在客户端扩展列表启用的独立第三方 MCP 以原生配置写入该次隔离 home;全局 Codex MCP、禁用项、Plugin hooks/apps 和其它插件能力不进入 DirectProject。第三方项固定非 required,配置或启动失败只记录该项,不替换 `agc_tools`;provider session token、工具桥地址和受控搜索标记不得通过第三方 MCP 的环境转发字段泄露。配置了 AGC LLM Key 或可解析的 `OPENAI_API_KEY` 登录态时,真实 provider 凭据只由 AGC 本地 provider proxy 持有,Codex 仅使用连接级随机代理令牌;无法安全代理的 OAuth `auth.json` 继续关闭 native shell/unified exec。`agc_tools` 的平台授权由 AGC 客户端当前登录会话和受控后端完成,普通客户端不得把 DirectProject 请求改成外部 API Key 请求;401/403 只投影为客户端登录或权限异常,不向用户索要凭据或暴露内部 URL。shell 子进程采用 `shell_environment_policy` core 继承及 secret/proxy/bridge 排除,provider key 和桥接凭据不得进入命令环境。系统提示词不再预注入项目源码快照或 Skill 正文,Codex 按需读取当前 cwd 文件。
|
||||
## 2026-08-24 AGC UI 原型桥接与自主 UI workflow
|
||||
@@ -1612,23 +1772,23 @@ Direct 回合的所有权属于进程内项目身份锁,不属于当前页面
|
||||
### 目标与非目标
|
||||
|
||||
- 目标:AGC 在项目工作区打开期间按固定周期把用户项目增量上传到 OSS `agc-dev`,并在项目关闭时立即补一次同步;重复内容不重复上传,远端占用跟随当前清单收敛。
|
||||
- 非目标:不做云端下载/恢复、不做跨设备合并、不保留多版本历史、不新增面向用户的上传界面、不修改 `/api/external/v1` 与 OpenAPI、不新增 SpacetimeDB 表。
|
||||
- 非目标:不做客户端云端恢复、不做跨设备合并、不保留多版本历史、不新增面向用户的上传界面、不修改 `/api/external/v1` 与 OpenAPI、不新增 SpacetimeDB 表。
|
||||
- 非目标:不把 OSS AccessKey 放进客户端;客户端不直连 OSS。
|
||||
|
||||
### 参与入口、状态与跨模块边界
|
||||
|
||||
- 触发入口有两个:工作区窗口 `main` 存活期间的周期定时器、工作区窗口关闭事件(`CloseRequested`)。两者共用同一个进程内同步器,同一项目的同步串行执行,周期触发在已有同步进行时直接让位,不排队堆积。
|
||||
- 应用退出(`RunEvent::Exit`)不重复发起同步:该时刻窗口已销毁,按窗口重新枚举项目只会得到空集;退出路径只负责在有界预算(15 秒)内等待在途同步收尾,让关窗触发的那一次同步能写完索引再退出。
|
||||
- 触发入口为项目生命周期登记、已登记项目的周期定时器及窗口关闭事件(`CloseRequested`)。它们共用同一个进程内同步器,同一项目的同步串行执行,周期触发在已有同步进行时直接让位,不排队堆积。
|
||||
- 应用退出(`RunEvent::Exit`)不重复发起同步;退出路径只负责在有界预算(15 秒)内等待在途同步收尾,让关窗触发的那一次同步有机会写完索引再退出。超过预算不能声明最后状态已经上传。
|
||||
- 客户端扫描、差异对比、索引持久化与上传编排都在 Tauri Rust 进程(`src-tauri/src/project_snapshot/`);WebView 只读状态,不参与差异计算。
|
||||
- 本地索引是增量对比的唯一依据:`<AppData>/project-snapshots/<projectId>/index.json` 保存上次成功同步的相对路径、校验和、字节数和修改时间。项目根使用现有 manifest 的稳定 `project_id` 作为远端身份,路径不再作为身份。
|
||||
- 可观测性按产品口径收敛到本机日志:同步结果、失败分类、延后与跳过计数只写入 AppData 诊断日志(`project_snapshot.sync.*` 前缀),客户端界面不暴露上传状态、时间线或入口按钮。`read_local_project_snapshot_state` 与 `sync_local_project_snapshot` 两条命令仅作为 native-only 的排障与联调入口登记,不在渲染层调用。
|
||||
- 远端写入经 `api-server`,客户端只持平台登录态 Access Token。两条登录态路由:`POST /api/agc/project-snapshots/files`(单文件,正文为原始字节,元数据走查询串)与 `POST /api/agc/project-snapshots/manifest`(本次同步后的完整清单)。
|
||||
- 对象键与清单由服务端决定:文件键为 `agc/project-snapshots/v1/{userId}/{projectId}/files/{sizeBytes}-{checksumDigest}/{relPath}`,清单键为 `agc/project-snapshots/v1/{userId}/{projectId}/manifest.json`。键里带字节数与摘要,因此"对象已存在且长度一致"可以作为内容一致的判据;路径按原始大小写保留,不走 `put_object` 的低位规范化。`agc` 前缀继续是服务端专用私有前缀,通用对象键解析与客户端直传票据都不覆盖它。
|
||||
- 目标 bucket 使用独立配置 `GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_BUCKET` / `_ENDPOINT` / `_ACCESS_KEY_ID` / `_ACCESS_KEY_SECRET`,默认 `agc-dev` + `oss-rg-china-mainland.aliyuncs.com`,未配置时回退 `ALIYUN_OSS_*`;与"资源 bucket 与备份 bucket 分离"的既有口径一致。
|
||||
- 目标 bucket 使用独立配置 `GENARRATIVE_AGC_PROJECT_SNAPSHOT_OSS_BUCKET` / `_ENDPOINT` / `_ACCESS_KEY_ID` / `_ACCESS_KEY_SECRET`,默认 `agc-dev` + `oss-rg-china-mainland.aliyuncs.com`。只允许凭据回退 `ALIYUN_OSS_ACCESS_KEY_ID` / `_ACCESS_KEY_SECRET`;bucket 与 endpoint 不跟随资源存储的 `ALIYUN_OSS_BUCKET` / `_ENDPOINT`,避免默认写入其它 bucket。显式快照目标配置继续优先;不自动搬迁其它 bucket 的现存数据。
|
||||
|
||||
### 正常、失败、重试与幂等行为
|
||||
|
||||
- 差异对比口径:先按 `相对路径 + 字节数 + 修改时间` 判定是否候选变更,命中旧记录则复用已存 `sha256`,只有 `(size, mtime)` 变化才重算摘要。产出新增、修改、删除三类集合,只上传新增与修改的文件。
|
||||
- 差异对比口径:先按 `相对路径 + 字节数 + 修改时间` 判定是否候选变更,命中旧记录则复用已存 `fnv1a64`,只有 `(size, mtime)` 变化才重算摘要。产出新增、修改、删除三类集合,只上传新增与修改的文件;清单元数据变化单独提交。
|
||||
- 每次成功同步的最后一步上传该项目的 `manifest.json`(当前全量文件清单:相对路径、摘要、字节数、同步序号)。清单描述的是项目当前全量内容,因此清单体积就是该项目在 OSS 上的常驻占用。
|
||||
- 远端回收:清单写入成功后,服务端读取上一版清单,按 `(路径, 字节数, 摘要)` 反推出不再被当前清单引用的对象键并删除。只处理上一版清单登记过的键,不做 LIST,因此不可能误删其它项目或其它功能的对象;单次最多回收 2000 个对象,剩余部分留到下一次清单写入继续;上一版清单读不到或解析失败时整轮跳过回收(fail-closed)。单个删除失败只记日志,不影响本次同步语义。
|
||||
- 因此本功能是"当前状态镜像 + 清单",不保留历史版本:同一路径的内容变化会覆盖式替换远端对象,回滚能力不在本轮范围内。
|
||||
@@ -1652,6 +1812,18 @@ Direct 回合的所有权属于进程内项目身份锁,不属于当前页面
|
||||
- 运行时 smoke:AGC 开发态打开项目、观察索引写入与同步日志、关闭工作区窗口后确认关闭触发的那次同步执行;报告为"客户端 diff 已验证 / 服务端已配置环境联调"两层,不合并成一句"已通"。
|
||||
- 边界:新增日志与错误文案不含 Access Token、AccessKey、绝对路径与项目内容。
|
||||
|
||||
### 后台工程列表与下载
|
||||
|
||||
- 正式客户端在单窗口内切换启动器和项目,不以窗口 URL 判断活动项目。前端把当前窗口的已打开项目登记给原生同步器;打开后发起首轮同步,离开/切换项目和物理关窗为旧项目补同步,周期扫描只读这份窗口登记。重复登记同一路径不重复发起;注册失败写诊断,不能假装已登记。
|
||||
- 清单补充可选 `projectName` 与 `pendingFiles`:名称来自本地 manifest;`pendingFiles` 是本轮失败、延后、并发变动与非策略排除的跳过文件数量。`0` 表示扫描范围已同步;大文件等被跳过不能标成完整。旧清单字段缺失表示完整性未知,维持可读取兼容,不反写旧清单。
|
||||
- 项目名称或完整性发生变化时,即使文件内容没有差异也要提交新清单;本机索引记录上次已提交的这两个字段。实际客户端下一次正常同步可补齐历史清单元数据;后台只读访问不迁移旧清单。缺失 `pendingFiles` 不能默认成 0,临时跳过原因消失后允许无文件上传的 `partial → ready` 转换。
|
||||
- 后台增加“项目工程”入口,仅 owner 及拥有 `project-snapshots` 页签权限的管理员可访问。`GET /admin/api/project-snapshots?cursor=&limit=20` 读取私有 OSS 清单并返回 `{items,nextCursor}`;单页最多 100 个,游标由服务端校验,目录与清单读取有界。条目为 `{userId,projectId,projectName,syncRevision,syncedAtMs,fileCount,totalBytes,status}`,状态为 `ready / partial / unverified`,名称缺失时显示 projectId。
|
||||
- `GET /admin/api/project-snapshots/{userId}/{projectId}/download` 只读取该用户/项目的固定清单与其引用对象,返回 `application/zip` 附件。ZIP 中路径直接使用原始相对路径,不包含 userId、摘要目录或 OSS 前缀;名称使用经过安全处理的项目名和 revision。下载固定本次读到的清单,远端并发回收导致对象缺失则整体失败,不能静默遗漏。
|
||||
- `partial` 快照下载返回 409;`unverified` 历史快照可导出已同步文件,列表明确显示“完整性未知”,动作称“下载已存文件”。`ready` 才显示“下载完整工程”。ZIP 构建核验每一文件的长度与 fnv1a64 摘要,拒绝穿越、绝对路径、重复/大小写冲突路径、非法项目身份;缺失或损坏整体失败,不返回成功的残缺 ZIP。
|
||||
- ZIP 使用服务端临时文件并限制并发,不将 2 GiB 工程整体驻留内存;成功、失败、客户端取消均清理临时文件。单文件、总量、文件数沿用上传上限,超限明确拒绝。零字节工程文件可以上传和导出。OSS 凭据与签名不下发浏览器,列表失败保留错误而非伪造空列表。
|
||||
- 工程归档范围与上传策略一致:源码、素材及引擎配置按原路径保存,依赖/构建缓存、凭据、`.agent` 会话与运行日志排除;此 ZIP 不声明能恢复 AGC 对话历史。后台读取需要目标前缀的 ListObjects 与 GetObject 权限,不新增数据库表,不改 External API。
|
||||
- 验收覆盖真实单窗口生命周期登记、首次/增量/零字节上传、后台列表分页和权限、ZIP 解压目录及摘要、部分/历史清单、缺失对象、路径拒绝、下载取消清理,并分别报告定向测试和真实环境证据。
|
||||
|
||||
### 未决问题
|
||||
|
||||
- 用户侧看不到同步状态与失败原因(界面按产品口径不暴露),排障只能读 AppData 诊断日志或调用 native-only 命令;如果后续要支持用户自助排查,需要先确认是否允许在客户端出现上传相关 UI。
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# 策划 Agent 生产迁移与工作区浏览方案
|
||||
|
||||
更新时间:2026-09-10
|
||||
状态:实施中
|
||||
更新时间:2026-09-20
|
||||
状态:已完成(2026-09-18)
|
||||
|
||||
> 现状说明(2026-09-18):本文记录的迁移已完成,当前策划入口统一使用 Design Agent。旧 Planning V1/V2 会话、专用命令、审批卡和展示适配已删除;文中提到的 V2 文件仅代表迁移时的参考来源,不得作为现行实现、回退路径或测试迁移目标。
|
||||
|
||||
## 1. 目标
|
||||
|
||||
@@ -23,11 +25,11 @@
|
||||
|
||||
生产侧复用 Provider、会话恢复、文件读写、审计和 UI 通信等基建,不复用现有立项策划 Agent 的行为协议。
|
||||
|
||||
原型行为是新的策划 Agent 契约。现有 `Planning V2` 只作为 Provider 调用、持久化和恢复实现的参考来源,不作为行为、提示词、产物或审批契约。
|
||||
原型行为是新的策划 Agent 契约。已退役的 `Planning V2` 仅作为迁移历史中的 Provider 调用、持久化和恢复参考来源,不作为行为、提示词、产物、审批契约或回退路径。
|
||||
|
||||
常驻提示词、阶段提示、速览卡结构说明、工具名称与参数、资源目录和注入映射以迁移时核对的原型文件为基线。迁移不顺便重写提示词,不增加模型输出内容门禁。旧需求文档中已明确舍弃的行为不恢复。
|
||||
|
||||
本方案描述生产迁移目标。当前已接入独立设计会话、自由工具循环、阶段审批命令和工作区浏览命令;生产入口对无旧 Planning V2 会话的策划项目切换到新设计 Agent,已有 V2 会话仍走原链路。旧 Planning V2 专用展示尚未清理。
|
||||
本方案描述已完成的生产迁移。当前入口使用独立设计会话、自由工具循环、阶段审批命令和工作区浏览命令;旧 Planning V2 会话不再继续运行,旧专用展示和命令已删除。历史项目按当前 Design Agent 入口重新开始,不做旧会话转换或旧测试迁移。
|
||||
|
||||
## 3. 复用与丢弃清单
|
||||
|
||||
@@ -66,16 +68,17 @@
|
||||
|
||||
路径穿越、绝对路径、控制目录访问和凭据泄露防护属于安全边界,可以保留;它们不能扩展成限制正常策划创作的业务门禁。
|
||||
|
||||
现有参考入口如下。复用对象是其中的可用函数和通信机制,不是整个模块:
|
||||
当前实现入口如下:
|
||||
|
||||
| 代码入口 | 参考内容与注意点 |
|
||||
| 代码入口 | 职责与边界 |
|
||||
| --- | --- |
|
||||
| `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_session_v2.rs` | Provider 配置、请求、重试、会话恢复;丢弃问询计数策略、强制工具和输出纠错协议 |
|
||||
| `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_tools/file_ops.rs` | 读写、补丁和删除实现;不可原封不动继承 owner、产物和任务门禁 |
|
||||
| `apps/ai-game-creator-shell/src-tauri/src/agent/design_runtime.rs` | Provider 请求、工具循环、重试、阶段审批与会话恢复 |
|
||||
| `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/design_session.rs` | 设计会话、阶段状态与待交互请求的持久化 |
|
||||
| `apps/ai-game-creator-shell/src-tauri/src/agent/design_tools.rs` | 固定资源包、工作区文件读写、补丁、删除、搜索与路径安全边界 |
|
||||
| `apps/ai-game-creator-shell/src-tauri/src/project/filesystem.rs` | 路径解析、文件列出和读取、基础写入;内部绝对路径字段不得传给模型 |
|
||||
| `apps/ai-game-creator-shell/src/App.tsx` | Tauri 事件订阅、文件刷新、会话恢复入口;不把旧策划状态投影当作新契约 |
|
||||
| `apps/ai-game-creator-shell/src/features/project-workspace/ProjectWorkspaceChatPane.tsx` | Game Agent 文件列表与读取入口;新的文件浏览应直接打开文件视图,不往聊天中追加文件正文 |
|
||||
| `apps/ai-game-creator-shell/src/features/project-workspace/GddApprovalCard.tsx` | 仅参考现有面板和通用交互样式;不沿用结构化 GDD 展示及三按钮决定模型 |
|
||||
| `apps/ai-game-creator-shell/src/App.tsx` | Design Agent IPC、事件订阅、文件刷新、会话恢复与输入提交 |
|
||||
| `apps/ai-game-creator-shell/src/features/project-workspace/DesignWorkspacePanel.tsx` | 策划工作区文件浏览与正文展示,直接打开文件视图 |
|
||||
| `apps/ai-game-creator-shell/src/features/project-workspace/DesignAgentSurface.tsx` | 消息、reasoning、澄清与阶段审批交互 |
|
||||
|
||||
不为此次迁移先建设通用 Agent 框架。已有函数能够直接使用就直接使用,只有确实需要拆除业务耦合时才做局部拆分。
|
||||
|
||||
@@ -113,7 +116,63 @@ Runtime 不维护文档版本号,不解析文档版本,不提供版本回退
|
||||
|
||||
会话/回合/工具调用身份用于生产恢复和重复请求处理,与 Agent 自行写在策划文档头部的版本号无关。
|
||||
|
||||
策划会话在创建时保存入口选择的 AGC 模型目录 ID(例如 `quality`、`fast`),同一会话后续回合沿用该 ID;客户端不保存或推断上游真实模型名。官方 `platform-llm` 直连 api-server 时携带 AGC 客户端标记,由 api-server 根据模型目录解析实际模型,不能在客户端硬编码某个上游模型替代目录选择。
|
||||
策划每次用户发起执行时采用客户端当前保存的模型和推理档,保存在当前回合中;同一执行内的工具调用与自动重试保持该选择,后续用户执行重新读取。官方模式保存 AGC 模型目录 ID(例如 `quality`、`fast`),自定义模式保存已选择的型号;客户端不推断官方上游真实模型名。官方 `platform-llm` 直连 api-server 时携带 AGC 客户端标记,由 api-server 根据模型目录解析实际模型,不能在客户端硬编码某个上游模型替代目录选择。
|
||||
|
||||
### 4.1 策划对话模型与推理档选择
|
||||
|
||||
**交付结果**:策划 Agent 对话复用 GameAgent 的模型、推理档选择控件及配置通道,用户的新选择对后续策划回合实际生效,并保持 GameAgent 原有行为兼容。
|
||||
|
||||
本节是本次变更的唯一主规范。控件接入已完成并提交,运行时模型生效逻辑已实现、待验收。拆分与验收见 [控件接入里程碑](../project-memory/plans/【里程碑】策划Agent模型与推理档控件接入-2026-09-20.md) 和 [模型生效里程碑](../project-memory/plans/【里程碑】策划Agent回合模型选择生效-2026-09-20.md)。
|
||||
|
||||
#### 范围与非目标
|
||||
|
||||
- 必须项:复用已有控件及客户端配置读写;补齐策划入口;修正旧策划会话固定使用创建时模型的行为;验证 GameAgent 兼容性。
|
||||
- 风险项:界面选择与实际请求不一致、执行中途切换配置、恢复旧会话,以及策划窄面板新增控件的布局。
|
||||
- 可选项:无。发现与上述验收无关的问题,只记录发现,不扩展本次实现。
|
||||
- 明确不做:不评估或重构 GameAgent 已有控件的本征不足,不重做模型目录、缓存、配置同步、下拉交互、通用保存队列或错误处理;不调整 GameAgent Runtime、推理默认值、供应商适配、提示词或阶段审批规则;不新建 Agent 专属设置、模型管理页、配置框架或测试框架。
|
||||
- 优先在现有共享对话容器中复用同一组组件,不复制控件源码,不因新增一个使用方迁移整套配置型组件。若需要局部接口扩展,默认调用必须保持 GameAgent 现有行为。
|
||||
|
||||
#### 控件和配置合同
|
||||
|
||||
1. 策划对话输入框附近显示与 GameAgent 相同的模型和推理档控件,读取、选项、保存、错误反馈沿用已有能力;只调整策划宿主必要的显隐和布局。按用户确认,控件保持原样,不给策划发送、审批、澄清或重试新增模型可用性检查,也不以模型目录状态新增提交门禁;GameAgent 已有提交逻辑保持原样。
|
||||
2. 继续使用客户端全局模型选择与全局推理档,不新增第二份策划设置。它们是客户端偏好,可能影响其它后续对话;进入或重开策划界面时显示当前保存值。既有 GameAgent 配置解析保持不变,不承诺新增跨窗口实时同步。
|
||||
3. 策划运行时最终以选择器保存的全局模型和推理档作为本次用户选择;策划专属底层配置不得在这两个字段上静默覆盖选择。连接、鉴权、超时等其余字段继续按既有生产配置解析,不清理或改写用户其它配置。
|
||||
4. 模型的官方目录别名、自定义模型目录、默认项跟随及失效项处理复用已有能力;不新增供应商能力探测或模型自动降级。推理档的枚举与默认值维持现状。
|
||||
5. 可以在执行中修改后续选择;不会中断或重启正在执行的策划回合。策划保留原有输入、发送、审批和澄清忙态规则,不引入 GameAgent 的消息队列、素材引用或语音功能。
|
||||
|
||||
#### 生效边界
|
||||
|
||||
| 触发 | 目标行为 |
|
||||
| --- | --- |
|
||||
| 首次发送、后续普通发送 | 读取最新已保存的选择并开始新回合,不新增提交前模型检查 |
|
||||
| 回答澄清、批准/拒绝阶段后实际继续调用 Provider | 同样使用本次继续前保存的选择;不更改审批结果和请求身份语义 |
|
||||
| 用户主动点击失败重试 | 使用最新选择开始本次执行;继续现有工具恢复规则,不重复已完成副作用 |
|
||||
| 同一执行内的工具循环、HTTP/流式瞬态自动重试 | 始终使用该次执行开始时确定的模型和推理档,不在每次 Provider 调用前重新采样这两个字段 |
|
||||
| 纯读取、展示历史、未触发 Provider 的操作 | 不创建新回合,不覆盖历史使用的模型信息 |
|
||||
| 进程中断后的自动续跑 | 优先沿用持久化的活动回合模型和推理档;不把它当成用户重新选模型后的新回合,不重复文件副作用 |
|
||||
|
||||
“后续回合生效”按上表定义,不以底层函数是否创建新 turn ID 为判断依据。无需重建会话或清空历史才能切模型,正式策划阶段、产物和上下文继续保留。
|
||||
|
||||
#### 失败、兼容与数据约束
|
||||
|
||||
- 控件保存失败和目录不可用沿用控件现有提示;策划发送、审批、澄清、重试和 Provider 报错流程保持原样。原控件内部的目录同步与默认模型处理不在本次改造范围,也不在策划宿主另加同类逻辑。
|
||||
- Provider 不接受所选模型或历史上下文时,沿用现有可见错误和重试;不静默换模型、清历史或另建跨模型上下文转换系统。
|
||||
- 已有设计会话无需离线迁移:下次用户发起执行时采用新选择。自动恢复的旧活动回合优先保留已有模型;若尚无推理档快照,使用当次有效配置补齐一次并固定,不伪称还原了历史档位。
|
||||
- 持久化在当前回合追加可缺省的 `modelSelection`,只含 `model` 和 `reasoningEffort`;已有会话 `modelId` 表示最近一次用户执行采用的模型,兼作旧活动回合恢复依据。旧记录缺字段可读;不保存完整配置、端点凭据、Token 或 API Key。不增加平行会话账本,不改模型目录 ID 与真实型号的边界。
|
||||
- 本次不涉及公开 HTTP API、OpenAPI 或 SpacetimeDB schema。若本地设计会话投影需要新增字段,同步其现有 Rust/TypeScript 定义和恢复用例。
|
||||
|
||||
#### 两步交付与验收
|
||||
|
||||
| 步骤 | 交付边界 | 完成证据 |
|
||||
| --- | --- | --- |
|
||||
| 第一步:控件接入 | 策划宿主显示并使用原控件,选择写入现有全局配置;保留策划原提交流程;不改策划请求的模型解析与快照逻辑 | 策划控件读写、忙态与失败提示定向测试;GameAgent 相关现有用例;宽/窄面板 smoke |
|
||||
| 第二步:模型生效 | 在策划执行边界采样并固定模型与推理档,旧会话下次执行采用新选择,自动恢复保留活动回合选择 | 本地 Provider fixture 验证真实请求字段、跨工具调用一致性、重试与恢复;界面到请求 smoke;GameAgent 兼容回归 |
|
||||
|
||||
第一步是内部可验收的接入结果,不能宣称“策划旧会话切模型已生效”,也不能作为完整功能单独发布。第二步验收前保持这一已知限制明确。
|
||||
|
||||
两个步骤分别形成最小闭环;检查点分别为“控件与兼容验收”和“请求与恢复验收”。实现中的新增发现只有影响本节交付判据时才扩大范围。每步先用一个工作时段完成定向实现与验证;超过时段仍未闭环时,说明剩余阻碍并重估,不以顺手改造组件扩大任务。
|
||||
|
||||
验收至少覆盖:保存后重新进入策划显示一致;旧会话切换模型后实际请求改变;当前执行不被中途改档;下一次发送/澄清/审批继续/主动重试生效;自动恢复不重复工具副作用;GameAgent 原有选择、发送和运行行为不回归。真实 Provider 或桌面环境缺失时明确记为未验证,不用 fixture 冒充实机结果。
|
||||
|
||||
## 5. Agent Runtime
|
||||
|
||||
@@ -177,6 +236,8 @@ concept → top_design → architecture → systems → tdd → consultant
|
||||
|
||||
模板、范例、类型资料和没有明确要求自动注入的文档继续保持选读。必读资源缺失、为空或读取失败时,只记录诊断并继续请求 Provider,不阻断阶段推进。
|
||||
|
||||
星露谷分析示例的资源路径统一为 `templates/stardew-analysis.md`,分册与示例中的引用保持一致;通过 `read_resource` 读取时使用目录登记的资源 ID `templates.stardew_analysis`。
|
||||
|
||||
根据当前原型 `resources/catalog.json`,自动注入清单为:
|
||||
|
||||
| 当前阶段 | 全文注入的资源 ID | 资源文件 |
|
||||
@@ -203,13 +264,15 @@ concept → top_design → architecture → systems → tdd → consultant
|
||||
|
||||
`systems: []` 是已确定的行为,不是迁移时需要补齐的缺口。`project/analysis.md`、`project/决策台账.md`、`project/dialog.md` 保持原型中的共享过程文件口径,不升级为新的审批必需项。速览卡保留原型结构提示,但 Runtime 与 UI 不解析其章节或内容字段。
|
||||
|
||||
过程文档记录关键依据、决定和待办,不要求实时完整,也不应重复正式设计文档;阶段提交前补齐影响验收的关键记录。顶层设计的分册、模板和示例保留核心定位及按需说明的易混淆方向与排除理由,不强制使用固定的“不是 X,而是 Y”句式。
|
||||
|
||||
进入下一阶段必须由用户批准触发。Runtime 推进后向 Agent 追加明确的用户行为语义,例如“用户已批准上一阶段,现在进入顶层设计阶段”,避免 Agent 误认为 Runtime 自行推进。
|
||||
|
||||
## 7. 审批与澄清交互
|
||||
|
||||
### 7.1 阶段审批
|
||||
|
||||
Agent 完成当前阶段后必须调用 `submit_phase_for_approval`。普通文本中的“批准”“确认”“进入下一阶段”等内容不改变 Runtime 阶段。
|
||||
Agent 完成五个策划阶段中的当前阶段后必须调用 `submit_phase_for_approval`。需要用户选择的关键问题先通过问询解决,阶段审批用于检阅已完成的产物;可以保留不阻塞当前阶段的后续事项和待原型验证项。普通文本中的“批准”“确认”“进入下一阶段”等内容不改变 Runtime 阶段。
|
||||
|
||||
提交时 Runtime 只检查:
|
||||
|
||||
@@ -231,7 +294,7 @@ UI 使用“批准”和“继续修改”两个文字按钮,分别配 Lucide
|
||||
- 选择“继续修改”后,再次出现新的审批请求前,不能重复批准旧请求;
|
||||
- 不实现 `/approve`、`批准`、`确认` 等文本检测。
|
||||
|
||||
批准与拒绝通过带请求身份的结构化命令处理。后端只接受当前待审批请求;重复点击同一已处理请求不再次推进,旧卡不能批准新的请求。此处校验请求身份与会话状态,不对文件增加指纹、快照或内容校验。进入顾问态后不自主安排新任务。
|
||||
批准与拒绝通过带请求身份的结构化命令处理。后端只接受当前待审批请求;重复点击同一已处理请求不再次推进,旧卡不能批准新的请求。此处校验请求身份与会话状态,不对文件增加指纹、快照或内容校验。顾问阶段遵照用户的具体指示行动,不自主推进项目或主动安排下一步,不提交阶段审批;完成单次用户请求不结束顾问态。
|
||||
|
||||
### 7.2 澄清
|
||||
|
||||
@@ -239,6 +302,8 @@ UI 使用“批准”和“继续修改”两个文字按钮,分别配 Lucide
|
||||
|
||||
澄清卡提供独立的选项回答和自由文本回答。点击选项只提交请求身份和所选选项,不携带文本框草稿;Runtime 根据已保存的问题/选项形成明确的回答内容,不只传递“3”之类的序号。用户也可不选择任何选项,直接填写文本并点击“提交回答”,发送请求身份、`optionIndex: null` 和文本正文,Runtime 将其表述为“用户回答”,不将它当作某个选项的补充。新澄清请求出现时清空上一题的文本草稿。不沿用生产旧问题数量上限和固定问答字段。澄清卡待回答状态应可在重启后恢复。
|
||||
|
||||
用户向上滚动查看策划历史时暂停自动跟随;发送新的普通消息后恢复跟随最新消息和回复,仅编辑输入内容不改变历史阅读位置。
|
||||
|
||||
## 8. 用户工作区浏览
|
||||
|
||||
`design_artifacts` 同时是 Agent 工作区和用户查看策划资料的文件区。用户不需要通过聊天请求 Agent 才能看到文件。
|
||||
@@ -307,7 +372,7 @@ UI 使用“批准”和“继续修改”两个文字按钮,分别配 Lucide
|
||||
|
||||
实现落在生产 Rust 会话/工具层与现有 React 策划入口,不随应用再启动 Python 原型进程。保持单一用户入口,不为迁移建设第二套工作台。
|
||||
|
||||
切换前盘点旧 Planning V2 活跃会话和持久化数据。旧结构化 GDD 不能直接推断为新五阶段中的某个阶段,不自动转换或覆盖已有项目。旧会话的继续运行或只读保留方式需在实际盘点后明确,再处理入口退役;这不要求新 Agent 兼容旧 GDD 行为。
|
||||
迁移时不把旧 Planning V2 会话或结构化 GDD 转换为新五阶段状态。旧命令、旧展示和旧测试已删除;当前入口不读取旧 V2 authority,也不要求新 Agent 兼容旧 GDD 行为。
|
||||
|
||||
## 12. 验收标准
|
||||
|
||||
@@ -340,7 +405,7 @@ UI 使用“批准”和“继续修改”两个文字按钮,分别配 Lucide
|
||||
- 外部搜索;
|
||||
- 多个策划身份提示词同时存在;
|
||||
- 多 Agent 协作;
|
||||
- 旧 Fast GDD 展示和 GDD schema 兼容。
|
||||
- 不迁移旧 Fast GDD 展示、V2 schema 或旧测试;新 Design Agent 使用自己的会话和阶段产物测试。
|
||||
|
||||
## 14. 开发调试入口
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# 策划会话 Runtime V2 接入与旧链路退役方案
|
||||
|
||||
- 日期:2026-09-03
|
||||
- 状态:P0 合同冻结、P1 内核、P2 产物闭环、P3 入口/UI 接入、P4 灰度回归验收与 P5 旧链路退役均已完成;本文是新生产实现的目标方案与阶段验收合同
|
||||
- 适用范围:AGC 桌面 App 的“做方案”入口、策划会话、GDD 产物与审批
|
||||
- 状态:**历史方案,已完成并退役**。Runtime V2 及其专用入口、命令、展示和测试已在 2026-09 按四不写原则删除;当前“做方案”统一使用独立 Design Agent。
|
||||
- 适用范围:历史 AGC“做方案”入口、策划会话、GDD 产物与审批设计
|
||||
|
||||
> 本文规定新策划 Agent 的生产接入和旧链路退役方式。P3 开始修改正式 AGC 入口与工作台,但旧 `project-supervisor-plan` / `project-planning` 源码仍保留,直到 P5 完成退役;V2 切换时旧链路直接封存,所有未完成旧会话强制失败,旧 Fast GDD 文档之后只作为历史记录依据。
|
||||
> 本文只用于追溯 Runtime V2 的设计和退役过程,不是现行实现依据。不要恢复 `planning_session_v2`、`planning_policy_v2`、`hydrate_planning_session_v2` 或 V2 专用 UI;当前行为以 Design Agent 生产迁移方案和代码为准。
|
||||
|
||||
## 1. 决策摘要
|
||||
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
# Responses API 与 Agents SDK 迁移评估
|
||||
|
||||
更新时间:`2026-09-07`
|
||||
|
||||
## 结论
|
||||
|
||||
本次评估不建议把 Genarrative 的核心 Agent Runtime 整体迁移到 OpenAI Agents SDK。建议保留 `platform-llm` 的 Responses / Chat / Anthropic 协议适配、Tauri/Rust Runtime、项目文件与进程工具、审批、权限与沙箱、持久会话、任务图、重试、恢复、计费和现有 Codex app-server 路径。
|
||||
|
||||
可以保留一个有明确边界的 Agents SDK 试验:只在可信的服务端或受控 Tauri bridge 中,为 Project Supervisor / 专业 Agent 的只读交互聊天提供可选的 `Agent + Runner` 编排、只读专家路由和统一 tracing。该试验必须通过现有 Runtime 工具网关和事件投影,不能让 SDK 直接读写项目、启动进程、提交媒体任务、扣费或持有正式状态。
|
||||
|
||||
理由是当前系统已经拥有比 SDK 默认能力更严格的业务控制面。技术方案已经明确规定 AGC Runtime 由 Genarrative 自己掌控,不把 OpenAI Agents SDK sidecar 作为核心,只借鉴 Agent、Tools、Handoffs、Guardrails、Tracing 抽象(见 `docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md` 的“技术选择”)。OpenAI 官方 Agents 文档也把 SDK 定位为建立在 Responses API 之上的编排层:SDK 用 `Agent` 和 `Runner` 管理 turns、tools、guardrails、handoffs 和 sessions;如果应用自己拥有这套循环,直接使用 Responses API 是合理路径。[Agents SDK Agents 指南](https://openai.github.io/openai-agents-python/agents/)
|
||||
|
||||
## 现状盘点
|
||||
|
||||
### Responses 协议层
|
||||
|
||||
`server-rs/crates/platform-llm/src/lib.rs` 定义了协议中立的 `LlmRunRequest` / `LlmRunResponse`。请求包含模型、system/user/assistant 消息、图像输入、`max_output_tokens`、web search、reasoning effort、text verbosity、function tools 和 `tool_choice`(约第 167–194 行)。默认 `LlmApiKind` 是 `openai_responses`,同时显式支持 `openai_chat` 和 `anthropic`。
|
||||
|
||||
Responses 请求在 `build_request_body` 中映射到 `/responses`,包括 `input`、`stream`、`max_output_tokens`、原生 `web_search`、function tools、`tool_choice`、`reasoning.effort` 和 `text.verbosity`(约第 2321–2404 行)。消息映射保持 Responses 的角色差异:system/user 文本使用 `input_text`,assistant 文本使用 `output_text`,图像使用 `input_image`(约第 2895–2973 行)。
|
||||
|
||||
非流式响应会从 `output_text` 和 `function_call` 中恢复正文、工具调用、usage、finish reason 和 response id。`LlmClient::run` 负责请求验证、HTTP 执行、响应读取和协议解析(约第 1550–1585 行)。
|
||||
|
||||
流式 `LlmClient::stream_run` 自己维护 SSE 解析、UTF-8 分块、Responses `response.output_text.delta`、`response.output_item.added`、`response.function_call_arguments.delta/done`、`response.completed` / `response.incomplete`、错误和尾部异常处理(约第 1596–1869 行)。工具调用按 Responses `output_index` 聚合,完整参数由终态事件校正;工具分片没有完成信号时会失败关闭;只把文本增量和 finish reason 交给上层,工具调用留在最终 `LlmRunResponse.tool_calls`。
|
||||
|
||||
`server-rs/crates/platform-llm/src/provider_adapter.rs` 把该 client 接入 `agent-runtime-core` 的 Provider registry。Responses 适配器宣告 Streaming、FunctionTools、RequiredToolChoice、ImageInput、WebSearch、ReasoningEffort 和 TextVerbosity 能力。`platform-llm/README.md` 明确规定该 crate 只负责文本协议适配,不承接上下文、后台执行、业务状态或工具副作用。
|
||||
|
||||
### AGC 的生成工作流
|
||||
|
||||
`apps/ai-game-creator-shell/src-tauri/src/agent/generation/loop_orchestration.rs` 仍保留一条文件驱动的确定性生成链:
|
||||
|
||||
1. Planner 读取用户需求、短期/长期记忆和项目黑板,生成 `.agent/spec.md`。
|
||||
2. Orchestrator 根据 spec、findings 和 manifest 生成 agenda / task graph。
|
||||
3. 按依赖 wave 并行生成设计、美术、程序等角色 brief,并写入本轮 pass 产物。
|
||||
4. Generator 根据 spec、findings、brief、素材和 agenda 生成结构化游戏草稿。
|
||||
5. Evaluator 写入 `.agent/findings.md`,失败时进入下一 pass 的修复范围。
|
||||
6. 产物写入、静态检查和试玩验证由 Runtime 完成,而不是由模型回复宣称完成。
|
||||
|
||||
Planner / Generator 使用独立的 system/user prompt 和严格结构化输出。Generator 选择 `run` 或 `stream_run`,但 legacy 生成 loop 的 streaming callback 为空,流式主要用于传输和容错;Generator 对 `EmptyResponse` 有有界重试,随后解析和校验 JSON。
|
||||
|
||||
这条链路的任务图、文件写入、Evaluator 反馈和产物门禁具有明确业务语义,不适合交给一个 SDK run loop 隐式管理。
|
||||
|
||||
### 后台 Runtime tool-plan 工作流
|
||||
|
||||
`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_request_builders.rs` 和 `provider_tool_plan.rs` 为每个后台 turn 动态构造请求:
|
||||
|
||||
- 读取当前 Agent、Session、Run、Goal Contract、Acceptance Graph、黑板、项目摘要、受控观察和运行中追加指令。
|
||||
- 按 Agent 身份、run profile、项目阶段、审批策略和验证门动态收窄工具目录。
|
||||
- 以严格 JSON Schema function tools 暴露 `file.*`、`project.*`、`command.*`、`preview.*`、`canvas.*`、`asset.*`、`memory.*`、`task.*`、`agent.*`、`goal_contract`、`acceptance_update`、`update_agent_plan` 和 `respond_to_user` 等能力。
|
||||
- 普通 Runtime tool-plan 要求模型直接调用白名单函数,并将计划更新、工具动作、观察和最终回复拆成可持久化的协议阶段。
|
||||
- 工具调用解析后进入 `agent/runtime_tools/*`,再经过权限、路径、项目写锁、确认、revision、验证和 receipt 门禁;模型不能直接执行副作用。
|
||||
- Provider 成功结果先写入 handoff / lifecycle / Agent DB,再由 Runtime 消费;重复、迟到、身份冲突或无法确定终态时进入 `needs-reconciliation`,不自动重放。
|
||||
|
||||
`agent_native_tools.rs` 生成严格工具 schema,工具策略与动态目录由 Runtime 控制。当前工具目录和工具执行已经是领域合同,不是简单的 JSON function-call wrapper。
|
||||
|
||||
### 交互聊天和流式行为
|
||||
|
||||
交互层在 `apps/ai-game-creator-shell/src-tauri/src/agent/interaction.rs` 中使用一个受限 interaction kernel:普通问答直接回复,只有确实需要宿主持久能力时才调用一个 function tool;高影响请求先澄清,不擅自启动 Runtime。它复用现有 Provider registry,流式异常可按稳定错误类型回退一次非流式请求。
|
||||
|
||||
Tauri command `chat_with_game_creator_role_agent_stream` 会先写入用户消息,再发出 `started` 事件,随后发出带 `delta_text`、`accumulated_text` 和 `finish_reason` 的 `delta` 事件,最终发出 `completed` 或 `failed`(`apps/ai-game-creator-shell/src-tauri/src/commands.rs` 约第 1199–1335 行)。React 侧按项目、Agent、Run 和加载代次过滤迟到事件,以 `requestAnimationFrame` 合并正文,失败时保留已接收的完整正文。
|
||||
|
||||
后台 final reply 还使用 `AgentRuntimeResponseStreamPublisher` 将流持久化到 `.agent/runtime/response-streams/<hashed-agent>/<hashed-run>.json`。记录绑定 Agent/Task/Session/Run、request slot、steer cursor、响应 revision、sequence、status、正文和 finish reason,并支持 `streaming → ready → committed/failed/discarded` 的恢复和去重。SDK 流必须先转译到这一合同,不能绕过它直接推送 UI。
|
||||
|
||||
### Codex app-server 路径
|
||||
|
||||
AGC 当前默认路线可以是 `codex_app_server`。`codex_app_server.rs` 通过 JSON-RPC `turn/start` / `turn/completed` 管理独立 thread/turn,消费 agent message、activity、item、MCP 工具和终态事件,并把增量正文映射回 `LlmStreamDelta`。`config.rs` 要求该模式使用 `apiKind=openai_responses`。
|
||||
|
||||
这条路径已经是另一种 agentic transport,且包含 workspace mode、进程生命周期、MCP 审计、硬超时和 passive-item 边界。它不能与 Agents SDK 直接互换;任何试验都必须保留该模式及其回滚能力。
|
||||
|
||||
### API Server、Router 和其他调用方
|
||||
|
||||
`server-rs/crates/api-server/src/llm/mod.rs` 提供账号鉴权的 `/api/llm/responses` 代理:客户端只传平台 access token,服务器解析账号 Router credential、模型目录和计费,删除客户端的 `apiKey`、`baseUrl`、provider、api kind、agent mode 等控制字段,再将 `stream`、`input`、`tools`、`metadata` 转发到 Responses endpoint。流式代理等待 `response.completed` / `response.incomplete` 后才结算额度。
|
||||
|
||||
`platform-editor-agent` 仍有自己的 `Agent`/memory/tool harness,但其编辑器对话目前固定使用 OpenAI Chat;图片、音频、UI 识别、画布生成等 helper 具有各自的严格结构化合同。它们不应因为 AGC 聊天试验而一起迁移。
|
||||
|
||||
## Prompt、工具和状态边界
|
||||
|
||||
### Prompt 分类
|
||||
|
||||
| 类别 | 当前入口 | 主要约束 | 迁移判断 |
|
||||
|---|---|---|---|
|
||||
| Planner / Generator | `generation/prompt_context.rs`、`loop_orchestration.rs` | 严格 JSON、文件产物、Evaluator pass、模型输出不能伪造写入/验证 | 保留 Responses / 当前编排 |
|
||||
| Runtime tool-plan | `runtime_actions/provider_request_builders.rs`、`prompt.rs` | 动态工具目录、身份策略、Goal/Acceptance、动作数量、确认和完成门 | 保留 Rust Runtime;SDK 只可作为未来只读 adapter |
|
||||
| Final reply | `provider_final_reply.rs`、`provider_request_builders.rs` | 只能依据 observation,不能声称未发生副作用;规划 Agent 有固定信封 | 保留现有路径,若试验仅转译文本事件 |
|
||||
| Interaction chat | `interaction.rs`、`prompt.rs` | 普通问答与单宿主工具决策;可流式;适合路由/澄清 | 唯一优先试验边界 |
|
||||
| Editor / media helpers | `platform-editor-agent`、`api-server/llm`、各媒体 prompt | Chat/Responses 混合、严格 JSON、计费和外部副作用 | 保留现有协议和专用 harness |
|
||||
|
||||
### 工具、委派和 handoff 的语义差异
|
||||
|
||||
当前 `agent.delegate` / isolated join 是持久任务调度动作,包含 parent/child ID、delivery、认领、all-join、恢复和完成门;Provider handoff sidecar 是跨 Runner 边界保存一次成功 Provider 响应。这两者都不等于 Agents SDK 的 handoff。
|
||||
|
||||
Agents SDK 的 handoff 是模型可见的工具式路由:一次 handoff 后由专业 Agent 接管同一 run 的对话;SDK 文档还区分了“manager + agents as tools”和“handoffs”。[Agents 组合模式](https://openai.github.io/openai-agents-js/guides/agents/)
|
||||
|
||||
因此:
|
||||
|
||||
- Supervisor 的只读问答可以使用 manager + `agent.asTool()`,保留 Supervisor 作为唯一面向用户的回复所有者。
|
||||
- 需要真正转移对话控制的只读专家咨询才考虑 SDK handoff,并通过 `inputFilter` 限定传入历史。
|
||||
- 任何涉及文件、命令、进程、媒体、审批、项目 revision、manifest、验证或 Git 的意图,必须先转成 Runtime 可验证的动作/委派请求,由 Rust 决定是否允许;SDK handoff 不能创建正式 child Run,也不能绕过 `agent.delegate`。
|
||||
|
||||
### 状态管理
|
||||
|
||||
当前正式状态由 `.agent/conversations/**/*.jsonl`、`.agent/runtime/**/*.json`、Agent DB、任务队列、manifest/revision、handoff/retry/confirmation ledger 和 finalization journal 组成。`AgentRuntimeProviderRequestSnapshot` 绑定 project/agent/task/session/run/source、goal revision/fingerprint、steer cursor、request kind/slot、web search 和 planning session binding。
|
||||
|
||||
Agents SDK 可以使用 `Session`、`conversationId`、`previousResponseId` 或 `RunState`,但官方文档提醒不要把多种历史策略叠加使用。[Agents SDK Sessions](https://openai.github.io/openai-agents-js/guides/sessions/)
|
||||
|
||||
本项目即使引入试验,也只能选择一个“临时模型上下文”策略;`.agent` 对话、Runtime sidecar 和 Agent DB 仍是唯一正式事实源。SDK session 不拥有 project revision、工具 action、审批、billing、retry、resume 或 finalization。重启恢复必须先恢复 Genarrative sidecar,再决定是否重建 SDK run。
|
||||
|
||||
## 是否需要更 agentic 的架构
|
||||
|
||||
核心 Runtime 已经具备更 agentic 的架构,而且它比通用 SDK loop 多了本项目所需的持久治理:
|
||||
|
||||
- 工具:严格 schema、身份目录、按角色/阶段动态收窄、auto/confirm/deny、项目锁和 revision。
|
||||
- 交接:静态专业 Agent、动态 isolated child、delivery receipt、all-join、恢复和 parent run 收束。
|
||||
- Guardrails:路径/文件类型、sandbox、权限、确认、Goal Contract、Acceptance Graph、验证 gate、最终回复门禁和敏感信息脱敏。
|
||||
- Tracing / audit:run trace、event、Agent DB、tool receipt、Provider lifecycle、request fingerprint 和 response stream sidecar。
|
||||
- Streaming:Responses SSE / Codex item events → Runtime stream publisher → Tauri events → 前端按 identity/sequence/revision 去重。
|
||||
- 状态:session catalog、JSONL conversation、Runtime state、task queue、retry/handoff sidecar、context compaction 和 crash recovery。
|
||||
|
||||
因此,整体迁移只会把已有能力复制一遍,带来两套 turn loop、两套状态、两套重试和两套工具授权;收益不足以抵消安全和恢复风险。
|
||||
|
||||
Agents SDK 对局部交互仍有增量价值:
|
||||
|
||||
| 能力 | 在本项目的真实收益 | 可接受边界 |
|
||||
|---|---|---|
|
||||
| Agent loop | 减少只读聊天中手写单轮/多轮和路由 glue code | 只用于交互聊天或只读专家问答 |
|
||||
| Tools | 统一工具参数解析、工具结果与模型 turn | function tool 只调用 Rust RPC 的窄只读 facade |
|
||||
| Handoffs / agents-as-tools | 更容易表达 Supervisor→专家的语言路由 | 不创建正式 Runtime child;推荐 manager + agents-as-tools |
|
||||
| Guardrails | 可增加聊天输入/输出格式和工具输入校验 | 不能替换 Rust 权限、确认、sandbox、revision gate |
|
||||
| Streaming | 提供 raw model、run item、agent updated 等统一事件 | 先转成现有 response-stream 记录,必须等待 `stream.completed` |
|
||||
| Tracing | 补充 generation/tool/handoff/guardrail latency 和 token span | 关联现有 run IDs,关闭敏感数据或自定义脱敏 processor |
|
||||
|
||||
官方 Streaming 文档明确说明 `toTextStream()` 只给正文,工具、handoff、approval 等需要消费完整事件流;流结束后还可能进行 session 持久化或 compaction,因此必须等待 `completed`。[Agents SDK Streaming](https://openai.github.io/openai-agents-js/guides/streaming/)
|
||||
|
||||
官方 Tracing 文档说明默认会记录 run/task/agent/turn、generation、function tool、guardrail 和 handoff spans,并支持自定义 trace processor;这对排查延迟和工具路由有价值,但也意味着 prompt 和工具数据必须显式设置敏感数据策略。[Agents SDK Tracing](https://openai.github.io/openai-agents-js/guides/tracing/)
|
||||
|
||||
## 推荐架构边界
|
||||
|
||||
```text
|
||||
Tauri / React
|
||||
│ 现有 invoke + Tauri events
|
||||
▼
|
||||
Genarrative Rust Runtime(正式事实源)
|
||||
├─ session / conversation / task / revision / approval / recovery
|
||||
├─ tool policy / sandbox / filesystem / process / media / billing
|
||||
├─ existing response-stream sidecar and Agent DB
|
||||
└─ Provider seam
|
||||
├─ current LlmClient + platform-llm (Responses/Chat/Anthropic)
|
||||
├─ Codex app-server (existing route)
|
||||
└─ optional Agents SDK adapter (experimental, read-only chat only)
|
||||
│
|
||||
└─ trusted Node/TS process or Tauri bridge; never browser bundle
|
||||
```
|
||||
|
||||
可选 adapter 的职责应是:接收 Rust 生成的已脱敏 prompt/context snapshot;创建有限的 `Agent`、工具和可选只读专家;调用 `run(..., {stream: true})`;把 SDK event 转成现有 `ProviderStreamEvent` / `LlmStreamDelta` / Runtime observation;将最终文本和工具意图返回 Rust。Adapter 不得读取项目绝对路径、凭据、签名 URL 或任意环境变量,也不得自行重试有副作用的工具。
|
||||
|
||||
建议在试验中优先使用 manager + agents-as-tools,而不是让 SDK handoff 成为对话所有权切换。这样 Supervisor 仍是唯一的用户回复所有者,SDK 专家只返回短的只读咨询结果。若确实需要 handoff,必须由 Rust 提前给出允许目标集合,并在 SDK handoff callback 中再次校验目标和输入;模型传入的 routing metadata 只能作为受限说明,不能改变权限或项目目标。
|
||||
|
||||
## 模型与 Provider 建议
|
||||
|
||||
OpenAI 官方模型页当前建议:复杂推理和编码优先 GPT-6 Astra,平衡能力与成本选择 GPT-5.6 Terra,高量低成本选择 GPT-5.6 Luna;最新模型可通过 Responses API 和 Client SDK 使用。[OpenAI Models](https://developers.openai.com/api/docs/models)
|
||||
|
||||
这不构成一次模型迁移任务:AGC 当前已有官方 Router / catalog alias 和每 Agent 配置,官方 route 已使用 `gpt-6-astra` 语义,客户端不能绕过服务器模型目录直接选择任意模型。迁移试验应冻结当前已配置模型和 reasoning/verbosity,先测编排差异;若要对 GPT-5.6 Terra/Luna 或其他新模型做基准,应作为独立模型评估,不与 SDK 迁移同时变更。
|
||||
|
||||
Agents SDK 试验只适合经过验证的 OpenAI Responses 路径。`openai_chat`、`anthropic`、VectorEngine/Ark 等兼容网关继续走 `platform-llm`,不要假设它们支持 SDK 的 hosted tools、server-managed conversation、Responses WebSocket 或完整事件形状。若自定义 base URL 不能稳定返回 function tool 和 SSE 终态事件,直接回退现有 Provider adapter。
|
||||
|
||||
## 测试与文档建议
|
||||
|
||||
现有测试应作为不可削弱的迁移门禁:
|
||||
|
||||
- `cargo test -p platform-llm --manifest-path server-rs/Cargo.toml`:Responses body、tool schema、图像输入、非流式 function call、SSE text/tool 聚合、completed/incomplete 恢复、参数截断、尾部错误、超时和重试。
|
||||
- `cargo test -p platform-agent-harness --manifest-path server-rs/Cargo.toml`:staged memory 提交/回滚、非法 JSON 修复、deadline、顺序工具和确认。
|
||||
- `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml provider -- --test-threads=1`:Provider、stream fallback、工具计划、retry、handoff、redaction 和 context compaction。
|
||||
- `apps/ai-game-creator-shell/src-tauri/src/tests/response_stream.rs` 及 `runtime_actions/response_stream_tests.rs`:stream sidecar 的 identity、sequence、reconnect、commit、discard、steer 和恢复。
|
||||
- `apps/ai-game-creator-shell/tests/appSurface/response-stream.suite.ts`、conversation/session、supervisor runtime、handoff、retry、runner-kill suites:UI 状态和真实恢复合同。
|
||||
- 现有 deterministic E2E 和 opt-in live Provider smoke 继续保留;live 测试不能成为默认 CI 依赖。
|
||||
|
||||
如果实施 SDK 试验,再增加:
|
||||
|
||||
1. **差分 fixture**:同一脱敏输入同时跑现有 Responses adapter 和 SDK adapter,比较最终文本、tool name/arguments、finish reason、usage、模型和错误分类;不执行真实副作用。
|
||||
2. **事件转译合同**:覆盖 raw model delta、message output、tool called/output、agent updated、handoff requested/occurred、approval interruption、completed 的顺序、重复和取消。
|
||||
3. **持久流恢复**:中途进程退出、tail error、重复事件、sequence 回退、steer cursor 变化和 finalization 竞态,都必须回到现有 sidecar 语义。
|
||||
4. **Session/RunState 隔离**:证明 SDK session/RunState 不能替换 `.agent/conversations`、`.agent/runtime` 和 Agent DB;恢复后不得重放已提交 file/media/process/billing action。
|
||||
5. **工具授权和幂等**:未经 Rust policy 允许的 tool/handoff 被拒;retry、handoff、approval resume 不产生重复副作用。
|
||||
6. **Tracing 脱敏**:trace processor 中不存在 API key、Cookie、签名 URL、绝对路径、原始工具参数、项目源码和用户私密正文;trace/group/workflow ID 可关联现有 project/session/run/agent ID。
|
||||
7. **兼容网关能力探测**:分别验证 HTTP Responses、function tools、SSE completed/incomplete、图像输入和错误事件;不因模型名推断 endpoint 能力。
|
||||
8. **回滚差分**:SDK 失败、超时、unsupported event、handoff 拒绝和 stream 取消都回到现有 `provider` 或 `codex_app_server`,且响应和 conversation 只落一次。
|
||||
|
||||
文档建议在真正开始试验时再更新:
|
||||
|
||||
- 为 AGC 增加 setup README:只在可信 Node/TS 进程或 bridge 安装并 pin `@openai/agents` 与 `zod`;说明 custom base URL、HTTP Responses transport、AppData credential、tracing key 与禁用/脱敏策略。SDK 不得打进浏览器 bundle,不能使用 Vite 暴露的 API key。
|
||||
- 更新 `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md` 的 LLM/Responses 段,补充 adapter feature flag、custom provider 能力限制、trace privacy、canary 与 rollback 命令。
|
||||
- 更新 `server-rs/crates/platform-llm/README.md`,声明 SDK 若加入只位于协议层之上的编排 adapter,Responses/Chat/Anthropic parser 和错误/stream 合同仍由 `platform-llm` 负责。
|
||||
- 新增迁移 runbook,写明 `.agent` 状态不迁移、不删除、不由 SDK 接管,测试矩阵和恢复证据要求。
|
||||
|
||||
## 分阶段迁移计划
|
||||
|
||||
### Phase 0:合同冻结(先做)
|
||||
|
||||
- 固定当前 `LlmRunRequest`、有效工具 schema、prompt bundle、model/api kind、reasoning/verbosity、stream 开关、retry/backoff、request fingerprint 和错误分类的 golden fixture。
|
||||
- 固定现有 response-stream sidecar 的 schema、identity、sequence、status、revision、steer cursor 和 finalization 行为。
|
||||
- 固定 `platform-llm`、AGC provider tests、response-stream tests、supervisor/handoff/recovery E2E 为 baseline。
|
||||
- 明确试验只允许 `provider` 的 OpenAI Responses 交互聊天;`codex_app_server`、Chat、Anthropic、legacy Planner/Generator 和项目副作用不进入首轮。
|
||||
|
||||
退出条件:所有 baseline 通过,且没有任何 SDK 依赖或状态格式变更。
|
||||
|
||||
### Phase 1:受控 SDK adapter(开发环境)
|
||||
|
||||
- 在受信任的 Node/TS 服务或 Tauri bridge 中增加可选 adapter;不要在 React/browser bundle 中调用 SDK。
|
||||
- Adapter 只收 Rust 传来的脱敏 context snapshot,使用 `Agent`、窄只读 function tools 和 manager-style `agents as tools`。
|
||||
- Rust 继续分配 project/session/run/request slot 和 action identity;SDK 的 run id、session 或 trace id 只作关联元数据。
|
||||
- 所有 tool function 必须调用 Rust RPC 的只读 facade;写文件、命令、进程、媒体、外部 editor、审批、billing 和 Git 均拒绝。
|
||||
- Adapter 将 SDK stream events 映射成既有 response-stream publisher 输入,等待 `stream.completed` 后才返回最终结果。
|
||||
|
||||
退出条件:没有 SDK 直连项目文件/凭据;只读聊天的 text/tool/stream parity fixture 通过;中止和重复事件不破坏 sidecar。
|
||||
|
||||
### Phase 2:影子差分与 tracing bridge
|
||||
|
||||
- 对脱敏的历史聊天 fixture 同时运行 current Provider 和 SDK adapter,但 SDK 侧不执行工具副作用。
|
||||
- 记录 final text、tool intent、事件延迟、首 token 延迟、token usage、错误类型、重试次数和取消行为,不记录原始 secret/prompt/tool payload。
|
||||
- SDK tracing 使用稳定 workflow name,例如 `genarrative-agc-interaction`;group id 绑定 session,metadata 只放哈希后的 project/agent/run/request identity。默认关闭敏感数据,或用自定义 processor 做字段级脱敏,并在任务结束 flush。
|
||||
- 统计差异而不是立即替换当前路径:结构化工具意图必须相同,文本差异需人工抽样,SDK 不得增加重复请求或扣费。
|
||||
|
||||
退出条件:在代表性 fixture 上,SDK 没有重复副作用、身份错配、公共信息泄漏、流式重复和不可解释的额外成本;否则停止试验。
|
||||
|
||||
### Phase 3:小流量可选 canary
|
||||
|
||||
- 用按 Agent / 项目 / build 的 feature flag 只开放给开发调试入口或内部用户。
|
||||
- 运行时检测 custom endpoint 是否支持 function tools、SSE terminal event 和目标 Responses 字段;不支持就选择旧 Provider。
|
||||
- SDK stream、handoff、approval 或 tracing 任一异常时,按“尚未产生副作用”条件回退;若已经产生 Rust action,必须先以 action ledger / reconciliation 处理,禁止盲目重试。
|
||||
- 保持同一个 UI event 和 conversation projection,不让用户看到两种状态模型。
|
||||
|
||||
退出条件:canary 的成功率、首 token 延迟、终态延迟、工具意图一致率、恢复成功率、重复副作用数、泄漏数和成本达到事先设定门槛,并连续多个版本稳定。
|
||||
|
||||
### Phase 4:仅在数据支持时扩大
|
||||
|
||||
最多把只读 Supervisor/role chat 扩到更多内部聊天场景。不要因此迁移:
|
||||
|
||||
- Planner→Generator→Evaluator 文件工作流。
|
||||
- 自主构建和正式 Runtime tool-plan。
|
||||
- `agent.delegate`、isolated child、all-join、DAG 和 manifest scheduler。
|
||||
- 文件、命令、进程、媒体、External Editor、Git、preview 和审批工具。
|
||||
- `platform-llm` 的 Chat/Anthropic/兼容网关和 API-server Router/billing。
|
||||
- Codex app-server DirectProject 路径。
|
||||
|
||||
只有在未来需求明确要求 SDK 的原生能力,并能证明它不会与上述合同冲突时,才另立架构决策评估核心迁移;本报告不预设该迁移。
|
||||
|
||||
## 回滚说明
|
||||
|
||||
- 首要回滚是关闭 feature flag,将该 Agent route 指回现有 `provider` 或 `codex_app_server`;不改 `.agent` 对话、Runtime state、Agent DB、manifest、revision 或 receipt schema。
|
||||
- SDK adapter 只允许生成可丢弃的临时 run/trace 记录。关闭后这些记录不参与恢复和最终回复;已有 Rust response-stream / finalization 记录继续按原逻辑收口。
|
||||
- 如果 SDK 已返回 tool intent 但 Rust 尚未提交 action,直接丢弃该意图并重新走旧 route;如果 Rust 已提交 action,必须按现有 receipt、ledger 和 reconciliation 恢复,不能用 SDK 重试覆盖。
|
||||
- 不通过自动降级重复调用可能产生计费或媒体副作用的请求。重试仍由现有 Runtime retry identity、attempt、backoff 和 provider handoff 控制。
|
||||
- 不把 SDK Session、Conversation ID、Previous Response ID 或 trace export 当作恢复依据;它们失效时不影响本地正式状态。
|
||||
|
||||
## 验证清单
|
||||
|
||||
- [ ] `platform-llm` 的 Responses request/response/SSE/tool parser 全量通过。
|
||||
- [ ] Chat、Anthropic、Codex app-server 和 legacy Planner/Generator 路径行为未变化。
|
||||
- [ ] SDK adapter 只在可信服务端/bridge 运行,浏览器 bundle 和前端环境变量没有 SDK key。
|
||||
- [ ] current Responses 与 SDK adapter 的 prompt、tool schema、tool choice、model、reasoning、verbosity 和 output budget 差分通过。
|
||||
- [ ] SDK stream event → `LlmStreamDelta` / `ProviderStreamEvent` → response-stream sidecar 的映射通过。
|
||||
- [ ] `started → delta → completed/failed` Tauri event 合同、sequence/revision/steer/session/run identity 和 UI 去重通过。
|
||||
- [ ] `stream.completed`、session persistence、approval interruption 和 cancellation 都被正确等待/收口。
|
||||
- [ ] SDK tool/handoff 不能绕过 Rust policy、confirmation、sandbox、project lock、revision、billing 或 finalization gate。
|
||||
- [ ] handoff/agents-as-tools 只作用于只读咨询;正式 `agent.delegate` 和 isolated join 仍由 Rust scheduler 管理。
|
||||
- [ ] 进程退出、网络尾错、重复事件、Runner kill、retry、steer 和 resume 不产生重复副作用或重复 assistant message。
|
||||
- [ ] trace 关联到现有 project/session/run/agent identity,敏感 prompt、路径、源码、凭据和 tool args 均不出现在 trace/export/log。
|
||||
- [ ] custom Responses endpoint 的 function tools、SSE terminal event、图像输入和错误事件能力已显式探测。
|
||||
- [ ] canary 指标满足门槛;若不满足,feature flag 可立即切回旧 route。
|
||||
- [ ] 文档已更新 setup、tracing privacy、provider capability、测试矩阵和 rollback runbook。
|
||||
@@ -7,12 +7,12 @@
|
||||
|
||||
## 1. 验收基线的先决条件
|
||||
|
||||
| 事实 | 依据 | 对验收的影响 |
|
||||
| --- | --- | --- |
|
||||
| 基线 commit 为 `ea9668e4a`(2026-09-11 15:37) | `git log -1`:`ea9668e4a`「资源总览「所有资源」卡补上资源预览」 | 验收记录表回填此 hash 作为冻结基线 |
|
||||
| 事实 | 依据 | 对验收的影响 |
|
||||
| --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 基线 commit 为 `ea9668e4a`(2026-09-11 15:37) | `git log -1`:`ea9668e4a`「资源总览「所有资源」卡补上资源预览」 | 验收记录表回填此 hash 作为冻结基线 |
|
||||
| 三个验收重点改动已入库,但**必须重新构建 / 重启客户端才会生效** | `2a157ea6f`「预览可见性门禁等 root 就绪再建 observer,并把登记表补挂齐」、`6bdc8bbd9`「AGC 资源分类面板改为「编辑素材标签」:只编辑标签、分类不再有手动入口」、`ea9668e4a`「资源总览「所有资源」卡补上资源预览」 | ① 可见卡停 `idle` 的修复;② 「编辑标签」只改 tags;③ 「所有资源」卡渲染真实预览。客户端若仍在跑旧构建(真机当前进程即属此列),这三条会得到**假失败** |
|
||||
| 工作区仍有 1 个非本用例范围的文件处于修改态 | `git status --short`:`apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts`(3 insertions / 2 deletions) | 只影响 AGC 全量测试结果,不影响手工验收;该文件相关用例失败时先与基线比对再定性 |
|
||||
| 真机项目落盘分类分布与旧构建指纹一致 | 真机 `.agent/manifest.json` 落盘 `category` = `unclassified 57 / ui-interaction 3 / scene 1` | 未用新构建复测时,卡片预览、标签面板、所有资源预览三条会得到**假失败** |
|
||||
| 工作区仍有 1 个非本用例范围的文件处于修改态 | `git status --short`:`apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts`(3 insertions / 2 deletions) | 只影响 AGC 全量测试结果,不影响手工验收;该文件相关用例失败时先与基线比对再定性 |
|
||||
| 真机项目落盘分类分布与旧构建指纹一致 | 真机 `.agent/manifest.json` 落盘 `category` = `unclassified 57 / ui-interaction 3 / scene 1` | 未用新构建复测时,卡片预览、标签面板、所有资源预览三条会得到**假失败** |
|
||||
|
||||
> 结论:验收开始前先固化"客户端构建版本 + api-server 是否重启 + 基线 commit"三件事,否则后续结论不可信。
|
||||
|
||||
@@ -28,72 +28,72 @@
|
||||
|
||||
### A 阶段 · 从零进项目
|
||||
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了"(可观察判据) | 已知例外 / 未做项 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| **S1** 建项 | 首页「做游戏」→ 输入需求 → 选择目录 → 确认为新项目 | 建项成功并直接进入项目工作台,不新建第二套页面或资产系统 | L7(四区)、L23(创作链路)、L108 | 进入后四区同时在 DOM:左侧平台导航、`.game-workbench-stage`(`aria-label="项目主视窗"`)、右侧 Supervisor 对话栏、底部 Agent Dock | 普通用户「新增资源」入口禁用(L108、L587) |
|
||||
| **S2** 落地视图 | 进项目后不动鼠标,看中央区 | 落 `resource-overview`;manifest 里 `preview.status=running` 的**陈旧记录**不得把你直接丢进运行界面 | L145–146、**L165**(`run` 自动进入只认内存 registry 活体确认) | `document.querySelector('.game-workbench-stage')?.dataset.resourceViewState` ∈ `resources.list` / `resources.selected.<category>` / `resources.ui-editor`;**该属性为 `undefined` 即说明已在 run 视图** | 陈旧记录对齐:命中"记录 running 但 registry 没跑"时调 `stop_local_game_preview` 并按真相投影 `{status:'stopped'}`(`sessionPreview.ts:87-101`) |
|
||||
| **S3** 项目页面板(可跳过) | 「项目组」页:搜索、行尾更多菜单、Escape 关闭 | 表格只显示权威数据列;Escape 关菜单并把焦点还给触发按钮;移除只改本机最近项目记录、不动磁盘 | L135–139 | 无匹配状态提供「清除搜索」;document/body 无页面级横纵向溢出 | **L139 要求 populated fixture 截图 + 视频演示搜索/清除/行尾菜单** —— 视频条款,见 7.1 |
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了"(可观察判据) | 已知例外 / 未做项 |
|
||||
| --------------------------- | -------------------------------------------------- | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **S1** 建项 | 首页「做游戏」→ 输入需求 → 选择目录 → 确认为新项目 | 建项成功并直接进入项目工作台,不新建第二套页面或资产系统 | L7(四区)、L23(创作链路)、L108 | 进入后四区同时在 DOM:左侧平台导航、`.game-workbench-stage`(`aria-label="项目主视窗"`)、右侧 Supervisor 对话栏、底部 Agent Dock | 普通用户「新增资源」入口禁用(L108、L587) |
|
||||
| **S2** 落地视图 | 进项目后不动鼠标,看中央区 | 落 `resource-overview`;manifest 里 `preview.status=running` 的**陈旧记录**不得把你直接丢进运行界面 | L145–146、**L165**(`run` 自动进入只认内存 registry 活体确认) | `document.querySelector('.game-workbench-stage')?.dataset.resourceViewState` ∈ `resources.list` / `resources.selected.<category>` / `resources.ui-editor`;**该属性为 `undefined` 即说明已在 run 视图** | 陈旧记录对齐:命中"记录 running 但 registry 没跑"时调 `stop_local_game_preview` 并按真相投影 `{status:'stopped'}`(`sessionPreview.ts:87-101`) |
|
||||
| **S3** 项目页面板(可跳过) | 「项目组」页:搜索、行尾更多菜单、Escape 关闭 | 表格只显示权威数据列;Escape 关菜单并把焦点还给触发按钮;移除只改本机最近项目记录、不动磁盘 | L135–139 | 无匹配状态提供「清除搜索」;document/body 无页面级横纵向溢出 | **L139 要求 populated fixture 截图 + 视频演示搜索/清除/行尾菜单** —— 视频条款,见 7.1 |
|
||||
|
||||
### B 阶段 · 素材产生与登记
|
||||
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| **S4** 首轮生成 | 在右侧 Supervisor 对话里发起首轮素材生成 | 生成由**对话启动**(不用画布按钮);素材写入对应栏目并登记进 manifest;未生成的素材不显示为可编辑对象 | L25、L94、L500;飞书 §4 首轮生成 | 对话完成后 `.agent/manifest.json` 的 `assets[]` 长度增加;新卡 `data-resource-card-id` 为 `asset:<id>` 形态 | 「首轮进度投影」只在 Issue 交付范围的"应该"清单出现、**无编码级判据**;现有可见性由底部 Agent 状态栏承载 |
|
||||
| **S5** 栏目归属 | 看资源总览的栏目缩略卡与各栏目卡片 | 固定 7 栏:`UI 交互 → 角色与对象 → 场景与环境 → 音频 → 文档 → 待归类 → 项目版本`;分区口径是 manifest `assets[].category`。**用户可经「编辑标签」面板的类型选择器改分类**(选择器显示读显示口径;写回:没碰过控件回传落盘原值、主动选过写用户选的值),改完卡片应落到新分类对应栏目、角标同步 | L50、L56、L324、L358–360、L539 | `[...document.querySelectorAll('.game-resource-book-thumbnail[data-resource-book-category]')].map(e=>e.dataset.resourceBookCategory)`;再数每栏 `.game-resource-card` 数量;角标读 `.game-resource-card-type-badge` 的 `data-resource-type` | **读时自愈**:落盘 `unclassified` 而 `kind` 能明确分类时按派生值显示(L360)。真机 57 条落盘 `unclassified` 在 UI 上应显示到正确栏目,**判定必须看 UI 计数,不能看 manifest 文件计数**。⚠ 反过来**不能**把自愈值当落盘值:判定「只改标签时分类有没有被改」必须比 manifest 文件(自愈只在读显示层,写回用 `gameCreationAppAssetPersistedCategory`)。⚠ 显式把 `kind` 已能明确分类的资产设为「待归类」会被自愈覆盖回派生栏目——已知盲区,不作为本用例的通过判据 |
|
||||
| **S6** 卡片预览台账 | 逐栏目看卡片是否显示真实主体 | 图片与安全 SVG 直接显示主体并保留透明棋盘底;视频、音频、文档、项目版本用稳定固定卡;失败显示类型占位、不挂破图、不降级为项目外 URL | L55、L61–62、**L67**、L347–349 | 见 5.1 的四条脚本:区分"没读 / 读失败 / 解码为空 / 不适用" | **可见卡停 `idle` 的真 bug 已修但未提交**(`useProjectResourceCardPreviews.ts`);未用新构建时会出现假失败 |
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| ------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **S4** 首轮生成 | 在右侧 Supervisor 对话里发起首轮素材生成 | 生成由**对话启动**(不用画布按钮);素材写入对应栏目并登记进 manifest;未生成的素材不显示为可编辑对象 | L25、L94、L500;飞书 §4 首轮生成 | 对话完成后 `.agent/manifest.json` 的 `assets[]` 长度增加;新卡 `data-resource-card-id` 为 `asset:<id>` 形态 | 「首轮进度投影」只在 Issue 交付范围的"应该"清单出现、**无编码级判据**;现有可见性由底部 Agent 状态栏承载 |
|
||||
| **S5** 栏目归属 | 看资源总览的栏目缩略卡与各栏目卡片 | 固定 7 栏:`UI 交互 → 角色与对象 → 场景与环境 → 音频 → 文档 → 待归类 → 项目版本`;分区口径是 manifest `assets[].category`。**用户可经「编辑标签」面板的类型选择器改分类**(选择器显示读显示口径;写回:没碰过控件回传落盘原值、主动选过写用户选的值),改完卡片应落到新分类对应栏目、角标同步 | L50、L56、L324、L358–360、L539 | `[...document.querySelectorAll('.game-resource-book-thumbnail[data-resource-book-category]')].map(e=>e.dataset.resourceBookCategory)`;再数每栏 `.game-resource-card` 数量;角标读 `.game-resource-card-type-badge` 的 `data-resource-type` | **读时自愈**:落盘 `unclassified` 而 `kind` 能明确分类时按派生值显示(L360)。真机 57 条落盘 `unclassified` 在 UI 上应显示到正确栏目,**判定必须看 UI 计数,不能看 manifest 文件计数**。⚠ 反过来**不能**把自愈值当落盘值:判定「只改标签时分类有没有被改」必须比 manifest 文件(自愈只在读显示层,写回用 `gameCreationAppAssetPersistedCategory`)。⚠ 显式把 `kind` 已能明确分类的资产设为「待归类」会被自愈覆盖回派生栏目——已知盲区,不作为本用例的通过判据 |
|
||||
| **S6** 卡片预览台账 | 逐栏目看卡片是否显示真实主体 | 图片与安全 SVG 直接显示主体并保留透明棋盘底;视频、音频、文档、项目版本用稳定固定卡;失败显示类型占位、不挂破图、不降级为项目外 URL | L55、L61–62、**L67**、L347–349 | 见 5.1 的四条脚本:区分"没读 / 读失败 / 解码为空 / 不适用" | **可见卡停 `idle` 的真 bug 已修但未提交**(`useProjectResourceCardPreviews.ts`);未用新构建时会出现假失败 |
|
||||
|
||||
### C 阶段 · 资源管理闭环
|
||||
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| **S7** 栏目导航 | 点总览第 0 张「所有资源」卡 → 栏目缩略卡片进栏目 → 页内底部「下一页」 | 「所有资源」是入口(第 0 张);进入后**与栏目页是同一套画本场景**——卡片宿主同为 `.game-resource-book-scene-card`、每张卡可拖、可滚轮缩放平移,各栏目按**竖向分带**平铺在同一张画布上(展开态**不出每栏一条标题栏**,整个场景只有 `all` 那一条钉在视口上的标题栏,与栏目页"一条钉住的标题栏 + 一张平铺画布"同形);全部栏目(含空栏目)都可到达;左侧悬浮栏目大纲导航已删除;滚轮不切栏目 | L56、L512、L539–541 | `[data-resource-book-view="main"\|"child"]`、当前栏目 `.game-resource-book-scene-titlebar.is-active[data-resource-book-category]`(展开态这一条是 `data-resource-book-category="all"`,带「资源总览」收起入口;**展开态 `data-resource-book-category` 非 `all` 的标题栏一个都不该有**——空栏目曾因拿不到分带矩形而集体回落到世界原点叠成一摞)、卡片宿主 `.game-resource-book-scene-card[data-resource-book-category="<真实栏目>"]`(每张资源**恰好一个**宿主)、`[data-resource-book-section-scroll]`。**旧的独立网格判据已随分支删除**:`[data-resource-book-all-page="true"]` / `[data-resource-book-all-host="true"]` / `.game-resource-all-*` 都不应再出现。拖动的判据:在展开态拖一张卡,再进它所属栏目页看同一张卡**落在新位置**(两页共用同一份 `resource-layouts/{dependency,type}.json` 的栏目内局部坐标) | 「所有资源」卡渲染真实预览(`game-resource-book-preview-card`)**已提交**(`ea9668e4a`);旧构建只有占位摞。展开态的视口是内存态(不落盘),只有卡片坐标落盘 |
|
||||
| **S8** 搜索 / 筛选 | 点右下角 `搜索资源`(放大镜)按钮或按 `Ctrl/Cmd+F` → 看到关键词 / 所在区域 / 自定义标签三字段同屏 → 输入 → Esc | Dock 上只有这一颗按钮,叫出的是**唯一**的筛选浮层(不存在第二个只放关键词的搜索浮层);浮层收起**不清筛选条件**;`type=search` 原生 Esc 清空被 `preventDefault` 挡掉;条件生效时 Dock 按钮高亮 | L55、L322(筛选只隐藏卡片、不重排坐标)、L329–330、L336–337 | `button[aria-label="搜索资源"]` 的 `aria-expanded`、`title="搜索资源(Ctrl/Cmd+F)"`、`.is-active`;浮层 `.game-resource-filter-panel`(`role="dialog"` 名「筛选资源」)内三字段 `查找素材` / `所在区域` / `自定义标签`;浮层容器 `id` 等于按钮的 `aria-controls`;Esc 后浮层不在 DOM、条件仍生效、焦点回到该按钮 | 匹配字段 = **名称 / 路径 / 媒体类型 / 任务标题**,不含 `sourceLabel` |
|
||||
| **S9** 选择与多选 | 单击卡片 → `Shift`/`Ctrl`/`Meta` 再点 → 空白处拖拽框选 | 点卡 = 选中并在卡片旁浮出工具条;三种修饰键等效追加;框选可见选框 | L51、L61–62、L318–319 | `data-resource-card-id` + class 含 `is-selected` + `aria-pressed="true"`;浮出容器 `role="toolbar"`(图片 `aria-label="图片工具栏"`,音频 `"素材工具栏"`) | ⚠️ **PRD L61–62 要求的「打开详情」按钮在代码中不存在**(测试显式断言为 `null`),按 Issue #309 C3「取消资源详情面板与全屏编辑路由」判,不要判为缺失;真正入口是 `aria-label="选中资源:<栏目> <名>"` 按钮。**只读信息浮层已回归**(2026-09-11,工具条「信息」动作,见 `decision-log`):**C3 取消的仍只是可编辑详情面板与全屏编辑路由** |
|
||||
| **S10** 快速编辑(图片) | 选中 PNG/JPEG/WEBP 卡 → 工具条「快速编辑」→ 输入 → 提交(提示词下方另有「AI 润色」) | 先 `normalize_local_project_raster_resource`,再 `derive_local_project_resource(editKind='image-reference')` → 服务端 `/api/editor/images/edits`;**产出一张新素材**,源素材与源文件不变,新资源带 `referenceResourceIds` 血缘 | L123、L193、L476–478、L557;#309 C3/C10 | 提交后 `assets[]` +1、卡片 +1 且 `data-resource-card-id` 为新 id;**源卡仍在且内容未变**;工具条真渲染的只有 `quick-edit` 与 `download`;「AI 润色」`aria-label="AI 润色"` + `aria-busy`,成功回填并出现「恢复原文」,失败保留原文并给「AI 润色失败,可重试」 | ① 服务端白名单 17 项(`editor_project.rs:4120-4141`),`character-animation`/`video`/`audio`/`document`/`code` 等**仍会 400**;② 工具条 7 个动作(重绘 / 裁剪扩图 / 去背景 / 像素完美 / 切图集 / 提取 UI 素材 / 角色动画)按 opt-in **不渲染**;③ 润色结果超 `resourceEditPromptMaxLength` 会**截断并提示「已按长度上限截断」**;④ 提示词变了(改写或润色回填)必须换 `operationId`/幂等键,否则 Rust `request_fingerprint` 判「已绑定到不同资源编辑请求」 |
|
||||
| **S11** 画布生成入口(视频) | 画布级入口 → 只呈现视频 → 填名与提示词 → 提交(提示词下方另有「AI 润色」) | 无源新建视频 → 新卡产出并定位(音效 / 背景音乐已移到栏目工具栏,见 S11a) | 飞书 §4(首轮由对话启动)、L122 | 提交后 `assets[]` +1;面板是独立浮层(非在当前面板下方追加);「AI 润色」走 `polish_local_project_prompt` 并带上「这是素材生成提示词(类型)」的场景约束,成功回填、失败保留原文 | ⚠️ **Issue #309 写「画布生成入口未接」,但当前代码已接三类**(`resourceCanvasGenerationModel.ts` 仅 `video`/`sound-effect`/`background-music`);图片等类型会直接报「当前资源类型不支持无源生成」;润色结果超该类型上限(背景音乐 140 / 音效 1900 / 视频 4000)会截断并提示「已按长度上限截断」 |
|
||||
| **S12** 编辑标签 | 选中已登记素材 → 工具条「编辑标签」→ 增删标签 → 保存 | 面板**只编辑 `manifest.assets[].tags`**;`category` 读一次权威值后**原样回传**;顶部没有分类 chip 排 | L360(2026-09-11 收口)、#309 C2 | 面板标题「编辑素材标签」,底部只有「取消 / 保存标签」;保存后 `assets[n].tags` 变化而 `assets[n].category` **逐字不变** | 面板内的「删除资源」按钮保留(删除入口另见 S15 的选中卡工具条);「用户手动设置 category」不再放在本面板,改为独立入口(工具条「素材类型」与信息浮层「分类 → 设置」,选中即落盘,见 decision-log 2026-09-14) |
|
||||
| **S13** 重命名 | 工具条「重命名」→ 新文件名 → 确认 | 磁盘文件名与 manifest `localPath` 更新;**asset id 不变**;写盘失败回滚文件 | L362(资源身份稳定)、L515 | 弹窗 `ariaLabel="重命名素材"`;改名后「选中资源:…」按钮的无障碍名同步变化;引用 chip 与 @ 候选列表显示名刷新 | ⚠️ **不改游戏源码里对旧 `assets/<name>` 的引用**,后果由用户自负 |
|
||||
| **S14** 下载 | 选中可下载卡 → 工具条「下载」 | 弹出**原生保存对话框**选目标路径 → 分块复制;取消即停止 | L507、飞书 §11.1.2 | 出现 `title="保存素材"` 的原生对话框;成功后状态提示含 `已保存 <路径>` | 语义是"另存为",不是静默直下;虚拟版本条目没有文件、不放行 |
|
||||
| **S15** 删除 | 工具条 / 标签面板「删除」→ 确认弹窗 | 三分支:① 无引用→直接删;② 被引用未勾选→只删素材、版本保留**悬空绑定**、界面不合成幽灵资源卡;③ 勾选→素材与该批版本在**同一次 manifest 写入**内一起删 | L413(版本只追加的唯一例外)、L532–533;#309 C5 | 弹窗 `ariaLabel="确认删除资源"`;被引用时出现 `被 N 个游戏版本使用` + 版本列表 + checkbox `把相关游戏版本一并删除`;无引用时该 body 整块不渲染 | **只摘登记、不删磁盘文件**;读引用命令精确名是 `read_local_project_asset_references` |
|
||||
| **S15a** 替换素材(含点选替换) | 选中被**当前版本**绑定的素材 → 工具条「替换素材」→ 候选弹窗(弹窗内可筛可选);再点 footer「点选替换」→ 弹窗关闭、进入画布点选态 → 在画布上直接点目标素材 | 弹窗确认与点选是**同一条**写入链路(`replace_local_project_version_resource`,载荷逐字一致):改该版本绑定、不建新版本;点选态下合法目标直接提交并自动退出点选态,非法目标零写入、**留在**点选态并在提示条上说明原因 | PRD §5.3 / §7.8 第 8 条;#309「直接替换」口径 | 点选态判据:候选弹窗 `role="dialog"` 已卸载;画布出现 `.game-resource-canvas-pick-hint`,文案含「在画布上点选要替换成的素材」与「点击空白处不会退出」,并有「取消」按钮;非法目标后提示条仍在且出现 `role="alert"`(「替换素材不兼容:分类不同」/「替换素材与源素材相同」/「替换素材未登记或已被删除」);点选期间卡片 `aria-pressed` 不变(单击只用于点选)、源素材选中与工具条不因点空白或点非法目标而丢;Esc 退出点选(画布全局 Esc 的清选中不随之触发);点选态下滚轮平移与缩放照常可用 | 点选只能点**当前画布上可见**的卡:目标在别的栏目时先切栏目(点选会话跨栏目存活,不因「收起资源」或切栏目结束);空白点击不退出、也不清画布选中 |
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **S7** 栏目导航 | 点总览第 0 张「所有资源」卡 → 栏目缩略卡片进栏目 → 页内底部「下一页」 | 「所有资源」是入口(第 0 张);进入后**与栏目页是同一套画本场景**——卡片宿主同为 `.game-resource-book-scene-card`、每张卡可拖、可滚轮缩放平移,各栏目按**竖向分带**平铺在同一张画布上(展开态**不出每栏一条标题栏**,整个场景只有 `all` 那一条钉在视口上的标题栏,与栏目页"一条钉住的标题栏 + 一张平铺画布"同形);全部栏目(含空栏目)都可到达;左侧悬浮栏目大纲导航已删除;滚轮不切栏目 | L56、L512、L539–541 | `[data-resource-book-view="main"\|"child"]`、当前栏目 `.game-resource-book-scene-titlebar.is-active[data-resource-book-category]`(展开态这一条是 `data-resource-book-category="all"`,带「资源总览」收起入口;**展开态 `data-resource-book-category` 非 `all` 的标题栏一个都不该有**——空栏目曾因拿不到分带矩形而集体回落到世界原点叠成一摞)、卡片宿主 `.game-resource-book-scene-card[data-resource-book-category="<真实栏目>"]`(每张资源**恰好一个**宿主)、`[data-resource-book-section-scroll]`。**旧的独立网格判据已随分支删除**:`[data-resource-book-all-page="true"]` / `[data-resource-book-all-host="true"]` / `.game-resource-all-*` 都不应再出现。拖动的判据:在展开态拖一张卡,再进它所属栏目页看同一张卡**落在新位置**(两页共用同一份 `resource-layouts/{dependency,type}.json` 的栏目内局部坐标) | 「所有资源」卡渲染真实预览(`game-resource-book-preview-card`)**已提交**(`ea9668e4a`);旧构建只有占位摞。展开态的视口是内存态(不落盘),只有卡片坐标落盘 |
|
||||
| **S8** 搜索 / 筛选 | 点右下角 `搜索资源`(放大镜)按钮或按 `Ctrl/Cmd+F` → 看到关键词 / 所在区域 / 自定义标签三字段同屏 → 输入 → Esc | Dock 上只有这一颗按钮,叫出的是**唯一**的筛选浮层(不存在第二个只放关键词的搜索浮层);浮层收起**不清筛选条件**;`type=search` 原生 Esc 清空被 `preventDefault` 挡掉;条件生效时 Dock 按钮高亮 | L55、L322(筛选只隐藏卡片、不重排坐标)、L329–330、L336–337 | `button[aria-label="搜索资源"]` 的 `aria-expanded`、`title="搜索资源(Ctrl/Cmd+F)"`、`.is-active`;浮层 `.game-resource-filter-panel`(`role="dialog"` 名「筛选资源」)内三字段 `查找素材` / `所在区域` / `自定义标签`;浮层容器 `id` 等于按钮的 `aria-controls`;Esc 后浮层不在 DOM、条件仍生效、焦点回到该按钮 | 匹配字段 = **名称 / 路径 / 媒体类型 / 任务标题**,不含 `sourceLabel` |
|
||||
| **S9** 选择与多选 | 单击卡片 → `Shift`/`Ctrl`/`Meta` 再点 → 空白处拖拽框选 | 点卡 = 选中并在卡片旁浮出工具条;三种修饰键等效追加;框选可见选框 | L51、L61–62、L318–319 | `data-resource-card-id` + class 含 `is-selected` + `aria-pressed="true"`;浮出容器 `role="toolbar"`(图片 `aria-label="图片工具栏"`,音频 `"素材工具栏"`) | ⚠️ **PRD L61–62 要求的「打开详情」按钮在代码中不存在**(测试显式断言为 `null`),按 Issue #309 C3「取消资源详情面板与全屏编辑路由」判,不要判为缺失;真正入口是 `aria-label="选中资源:<栏目> <名>"` 按钮。**只读信息浮层已回归**(2026-09-11,工具条「信息」动作,见 `decision-log`):**C3 取消的仍只是可编辑详情面板与全屏编辑路由** |
|
||||
| **S10** 快速编辑(图片) | 选中 PNG/JPEG/WEBP 卡 → 工具条「快速编辑」→ 输入 → 提交(提示词下方另有「AI 润色」) | 先 `normalize_local_project_raster_resource`,再 `derive_local_project_resource(editKind='image-reference')` → 服务端 `/api/editor/images/edits`;**产出一张新素材**,源素材与源文件不变,新资源带 `referenceResourceIds` 血缘 | L123、L193、L476–478、L557;#309 C3/C10 | 提交后 `assets[]` +1、卡片 +1 且 `data-resource-card-id` 为新 id;**源卡仍在且内容未变**;工具条真渲染的只有 `quick-edit` 与 `download`;「AI 润色」`aria-label="AI 润色"` + `aria-busy`,成功回填并出现「恢复原文」,失败保留原文并给「AI 润色失败,可重试」 | ① 服务端白名单 17 项(`editor_project.rs:4120-4141`),`character-animation`/`video`/`audio`/`document`/`code` 等**仍会 400**;② 工具条 7 个动作(重绘 / 裁剪扩图 / 去背景 / 像素完美 / 切图集 / 提取 UI 素材 / 角色动画)按 opt-in **不渲染**;③ 润色结果超 `resourceEditPromptMaxLength` 会**截断并提示「已按长度上限截断」**;④ 提示词变了(改写或润色回填)必须换 `operationId`/幂等键,否则 Rust `request_fingerprint` 判「已绑定到不同资源编辑请求」 |
|
||||
| **S11** 画布生成入口(视频) | 画布级入口 → 只呈现视频 → 填名与提示词 → 提交(提示词下方另有「AI 润色」) | 无源新建视频 → 新卡产出并定位(音效 / 背景音乐已移到栏目工具栏,见 S11a) | 飞书 §4(首轮由对话启动)、L122 | 提交后 `assets[]` +1;面板是独立浮层(非在当前面板下方追加);「AI 润色」走 `polish_local_project_prompt` 并带上「这是素材生成提示词(类型)」的场景约束,成功回填、失败保留原文 | ⚠️ **Issue #309 写「画布生成入口未接」,但当前代码已接三类**(`resourceCanvasGenerationModel.ts` 仅 `video`/`sound-effect`/`background-music`);图片等类型会直接报「当前资源类型不支持无源生成」;润色结果超该类型上限(背景音乐 140 / 音效 1900 / 视频 4000)会截断并提示「已按长度上限截断」 |
|
||||
| **S12** 编辑标签 | 选中已登记素材 → 工具条「编辑标签」→ 增删标签 → 保存 | 面板**只编辑 `manifest.assets[].tags`**;`category` 读一次权威值后**原样回传**;顶部没有分类 chip 排 | L360(2026-09-11 收口)、#309 C2 | 面板标题「编辑素材标签」,底部只有「取消 / 保存标签」;保存后 `assets[n].tags` 变化而 `assets[n].category` **逐字不变** | 面板内的「删除资源」按钮保留(删除入口另见 S15 的选中卡工具条);「用户手动设置 category」不再放在本面板,改为独立入口(工具条「素材类型」与信息浮层「分类 → 设置」,选中即落盘,见 decision-log 2026-09-14) |
|
||||
| **S13** 重命名 | 工具条「重命名」→ 新文件名 → 确认 | 磁盘文件名与 manifest `localPath` 更新;**asset id 不变**;写盘失败回滚文件 | L362(资源身份稳定)、L515 | 弹窗 `ariaLabel="重命名素材"`;改名后「选中资源:…」按钮的无障碍名同步变化;引用 chip 与 @ 候选列表显示名刷新 | ⚠️ **不改游戏源码里对旧 `assets/<name>` 的引用**,后果由用户自负 |
|
||||
| **S14** 下载 | 选中可下载卡 → 工具条「下载」 | 弹出**原生保存对话框**选目标路径 → 分块复制;取消即停止 | L507、飞书 §11.1.2 | 出现 `title="保存素材"` 的原生对话框;成功后状态提示含 `已保存 <路径>` | 语义是"另存为",不是静默直下;虚拟版本条目没有文件、不放行 |
|
||||
| **S15** 删除 | 工具条 / 标签面板「删除」→ 确认弹窗 | 三分支:① 无引用→直接删;② 被引用未勾选→只删素材、版本保留**悬空绑定**、界面不合成幽灵资源卡;③ 勾选→素材与该批版本在**同一次 manifest 写入**内一起删 | L413(版本只追加的唯一例外)、L532–533;#309 C5 | 弹窗 `ariaLabel="确认删除资源"`;被引用时出现 `被 N 个游戏版本使用` + 版本列表 + checkbox `把相关游戏版本一并删除`;无引用时该 body 整块不渲染 | **只摘登记、不删磁盘文件**;读引用命令精确名是 `read_local_project_asset_references` |
|
||||
| **S15a** 替换素材(面板内选 + 画布点选目标) | 选中被**当前版本**绑定的素材 → 工具条「替换素材」→ 画布右上角出现非模态候选面板(面板内可筛可选)→ 直接在画布上点目标素材(或点面板里的候选)→ 面板「确认」 | 面板确认与画布点选是**同一条**写入链路(`replace_local_project_version_resource`,载荷逐字一致):改该版本绑定、不建新版本;画布点选只把目标落成面板的当前选择,确认后才写入 | PRD §5.3 / §7.8 第 8 条;#309「直接替换」口径 | 面板判据:`role="dialog"` 名为「选择替换素材」、挂 `.image-canvas-editor__project-asset-picker--floating`、**没有** `.platform-overlay` 遮罩;点画布候选后面板里该候选 `aria-selected="true"`、面板里的搜索词与分类筛选保持原样、零写入;非法目标(源素材本身 / 不在权威候选里 / 分类不同 / 未登记资源)在面板里出现 `role="alert"`(「替换素材与源素材相同」/「替换素材未登记或已被删除」/「替换素材不兼容:分类不同」)且零写入;面板关闭 = 卸载;Esc 只收面板(画布全局 Esc 的清选中不随之触发) | 点选只能点**当前画布上可见**的卡:目标在别的栏目时先切栏目(替换会话跨栏目存活,不因「收起资源」或切栏目结束);面板开着时空白处仍可框选 / 平移,卡片单击语义的恢复发生在面板关闭之后 |
|
||||
|
||||
| **S11a** 栏目画布底部工具栏 | 进「UI 交互 / 角色与对象 / 场景与环境 / 音频」任一栏目 → 点左下角工具栏里的入口生成 → 「收起资源」回总览看工具栏消失 | 工具栏只在矩阵四个栏目(功能画布)渲染;图片类入口走 `start_local_project_asset_generation`(提交即返回、生成在后台跑),音频入口复用既有无源生成链路,上传复用 `upload_local_asset`;生成 / 上传成功后走既有 manifest 刷新与资源定位 | PRD §3.10、§7.9 | DOM 判据 `[data-resource-bottom-toolbar="<category>"]`(资源总览、「所有资源」展开态、文档、待归类、项目版本都**没有**这个节点);工具栏是 `.game-resource-book-manager` 的直接子节点(`closest('.game-resource-book-scene')` 为 `null`);每个入口一次 `start_local_project_asset_generation`,载荷逐字为 `{ projectPath, projectId, taskId, kind, prompt, aspectRatio, imageSize, assetName, outputPath }`(`kind` 映射见 PRD §7.9 第 3 条;`taskId` 是前端每次提交新铸的本地任务 id);该命令提交即返回,之后有 `list_local_project_asset_generations` 轮询与 `get_local_game_project_revision` + `get_local_game_manifest` 的配对读;提交期间点 × / 遮罩 / Esc /「后台运行并关闭」任一都能关面板且请求继续(画布立即恢复可交互),关闭后任务仍出现在「生成任务」面板(入口按钮 `aria-label="生成任务"`,非模态浮层、无 `aria-modal`)并显示后端 `phaseDetail`;第一条未终态时提交第二条 → 第二条显示「排队中。」且生成提交 IPC 次数仍为 1,第一条终态后自动补发(次数变 2);缺 `assets/art-spec.png` 时「生成图标素材 / 生成 UI 设计图」仍可点击(`aria-disabled="true"` 但**不是**原生 disabled)并给出含该路径的 `role="alert"` 原因、零生成请求;音频入口面板标题即「生成背景音乐」/「生成音效」且没有类型选择器 | ① 本地通道没有 `model` / `specType` / `replaceExisting` 入参:面板不渲染模型选择器,角色规范与自定义规范共用 `spec` 通道(靠 `assetName` 与提示词区分),「图标规范」在项目已有权威规范图时不再指向 `assets/art-spec.png`(否则会被 Rust 的防覆盖校验硬拒);② 本轮不做生成视频 / 宣发素材 / 生成游戏场景 / 选择工具 / 抓手工具;③ 既有「生成素材」浮层入口只保留生成视频,音频入口只在音频栏目工具栏出现 |
|
||||
| **S11a** 栏目画布底部工具栏 | 进「UI 交互 / 角色与对象 / 场景与环境 / 音频」任一栏目 → 点左下角工具栏里的入口生成 → 「收起资源」回总览看工具栏消失 | 工具栏只在矩阵四个栏目(功能画布)渲染;图片类入口走 `start_local_project_asset_generation`(提交即返回、生成在后台跑),音频入口走同一条命令的音频载荷,上传复用 `upload_local_asset`;生成 / 上传成功后走既有 manifest 刷新与资源定位 | PRD §3.10、§7.9 | DOM 判据 `[data-resource-bottom-toolbar="<category>"]`(资源总览、「所有资源」展开态、文档、待归类、项目版本都**没有**这个节点);工具栏是 `.game-resource-book-manager` 的直接子节点(`closest('.game-resource-book-scene')` 为 `null`);每个入口一次 `start_local_project_asset_generation`:图片类载荷逐字为 `{ projectPath, projectId, taskId, kind, prompt, aspectRatio, imageSize, assetName, outputPath }`(`kind` 映射见 PRD §7.9 第 3 条;`taskId` 是前端每次提交新铸的本地任务 id),音频类载荷为 `{ projectPath, projectId, taskId, kind, prompt, assetName, idempotencyKey }`(`taskId` 即该次生成的 operation id,不发图片类那套比例 / 尺寸 / 参考 / 落点参数);该命令提交即返回,之后有 `list_local_project_asset_generations` 轮询与 `get_local_game_project_revision` + `get_local_game_manifest` 的配对读;点「生成」即把这次输入交给后台账本并**同步关闭**面板(不等 IPC、不等排队、不等生成,画布立即恢复可交互;面板 DOM 里没有阶段文案与「后台运行并关闭」这类在途按钮),关闭不等于取消,关闭后任务仍出现在「生成任务」面板(入口按钮 `aria-label="生成任务"`,非模态浮层、无 `aria-modal`)并显示后端 `phaseDetail`;第一条未终态时提交第二条 → 第二条显示「排队中。」且生成提交 IPC 次数仍为 1,第一条终态后自动补发(次数变 2);缺 `assets/art-spec.png` 时「生成图标素材 / 生成 UI 设计图」仍可点击(`aria-disabled="true"` 但**不是**原生 disabled)并给出含该路径的 `role="alert"` 原因、零生成请求;音频入口面板标题即「生成背景音乐」/「生成音效」且没有类型选择器 | ① 本地通道没有 `model` / `specType` / `replaceExisting` 入参:面板不渲染模型选择器,角色规范与自定义规范共用 `spec` 通道(靠 `assetName` 与提示词区分),「图标规范」在项目已有权威规范图时不再指向 `assets/art-spec.png`(否则会被 Rust 的防覆盖校验硬拒);② 本轮不做生成视频 / 宣发素材 / 生成游戏场景 / 选择工具 / 抓手工具;③ 既有「生成素材」浮层入口只保留生成视频,音频入口只在音频栏目工具栏出现 |
|
||||
|
||||
### D 阶段 · 版本与运行
|
||||
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| **S16** 版本切换 | 运行模块**右上角**版本选择器 → 切到另一版本 | 触发钮显示当前版本;切换后资源卡「当前使用」高亮与 **@ 面板「当前版本素材」同步更新**,并重载当前预览 | L165、L190、L324、L530;#309 C7 | `aria-label="当前版本:<label>"`、菜单 `role="listbox" aria-label="切换游戏版本"`、选项 `role="option" aria-selected`;`[data-used-by-current-version="true"]` 集合随切换变化 | **C7 只做记录层 + UI 层**;"历史版本独有资源集合被运行时按版本加载"需新增版本化资源解析机制,**本轮未实现**;版本卡卡面**不渲染**「项目修订 / 父版本」文字 |
|
||||
| **S17** 播放 | 顶部「播放」 | 直接切运行视图并启动本地预览;**不再弹 `game.run_local` 二次确认**;预览占满中央可用区、无 iframe 滚动条 | L16、L71–72、L122、L493 | `data-resource-view-state` 变为 `undefined`;`await window.__TAURI__.core.invoke('get_local_game_preview_status',{projectPath})` → `status==='running'` 且有 `url`(形如 `http://127.0.0.1:<随机端口>/`) | 播放按钮 `disabled={!runAvailable \|\| !onPlay \|\| uiEditorRoute!==null}`,**不可用时点不动**;不可用提示实现文案为「首个可运行原型尚未完成,运行视图暂不可用」,与 PRD L165 表述不同(见 7.4) |
|
||||
| **S18** 停止与退出收尾 | 聊天侧工程操作组点「停止预览」;或关闭客户端 | 预览停止、manifest `preview` 对齐 `stopped`(清 url/port);退出时也统一收尾 | L165 | 停止后 `get_local_game_preview_status` 非 running;`.agent/logs/preview.log` 追加 `stopped`;退出后重开同一项目应落回 `resource-overview` | 停止按钮**不在**运行视图工具栏,在工作台右侧工程操作组;退出失败会打 `preview.gui_exit.stop_failed` |
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| ---------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **S16** 版本切换 | 运行模块**右上角**版本选择器 → 切到另一版本 | 触发钮显示当前版本;切换后资源卡「当前使用」高亮与 **@ 面板「当前版本素材」同步更新**,并重载当前预览 | L165、L190、L324、L530;#309 C7 | `aria-label="当前版本:<label>"`、菜单 `role="listbox" aria-label="切换游戏版本"`、选项 `role="option" aria-selected`;`[data-used-by-current-version="true"]` 集合随切换变化 | **C7 只做记录层 + UI 层**;"历史版本独有资源集合被运行时按版本加载"需新增版本化资源解析机制,**本轮未实现**;版本卡卡面**不渲染**「项目修订 / 父版本」文字 |
|
||||
| **S17** 播放 | 顶部「播放」 | 直接切运行视图并启动本地预览;**不再弹 `game.run_local` 二次确认**;预览占满中央可用区、无 iframe 滚动条 | L16、L71–72、L122、L493 | `data-resource-view-state` 变为 `undefined`;`await window.__TAURI__.core.invoke('get_local_game_preview_status',{projectPath})` → `status==='running'` 且有 `url`(形如 `http://127.0.0.1:<随机端口>/`) | 播放按钮 `disabled={!runAvailable \|\| !onPlay \|\| uiEditorRoute!==null}`,**不可用时点不动**;不可用提示实现文案为「首个可运行原型尚未完成,运行视图暂不可用」,与 PRD L165 表述不同(见 7.4) |
|
||||
| **S18** 停止与退出收尾 | 聊天侧工程操作组点「停止预览」;或关闭客户端 | 预览停止、manifest `preview` 对齐 `stopped`(清 url/port);退出时也统一收尾 | L165 | 停止后 `get_local_game_preview_status` 非 running;`.agent/logs/preview.log` 追加 `stopped`;退出后重开同一项目应落回 `resource-overview` | 停止按钮**不在**运行视图工具栏,在工作台右侧工程操作组;退出失败会打 `preview.gui_exit.stop_failed` |
|
||||
|
||||
### E 阶段 · 对话侧
|
||||
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| **S19** @ 引用 | 输入区「@」→ 两个页签 + 筛选(功能分类 + 多标签)+ 搜索 + 多选 → 「添加到对话」 | 两页签**各自独立**的搜索与筛选状态(含各自一份标签库),**不影响**资源画布筛选;已选素材不因筛选或搜索隐藏而被取消;chip 整体插入 | L187、L420–426、L453–461;#309 C4 | `role="tablist"`(`aria-label="素材范围"`)内两个 `role="tab"`:`当前版本素材` / `全部画布素材`;输入框 aria-label 分别为 `搜索当前版本素材` / `搜索全部画布素材`;标签行是共享筛选条里 `aria-label="素材筛选标签"` 的分组,chip 用 `aria-pressed` 表达选中;chip = `span.resource-reference-chip[data-resource-reference-id]` | **chip 原子性有正式定义**:chip 是 Lexical `DecoratorNode`,纯文本表示固定占 **1 个字符** `U+FFFC` ⇒ 一次 Backspace 只能整体删掉一个 chip,不可能删半截;引用身份是稳定 `resourceId`,改名后按 id 刷新显示名。**多标签是 AND**(口径来自 `packages/shared` 的 `assetTagsMatchSelection`,标签取自 manifest `assets[].tags`),并与搜索词、功能分类三者叠加 |
|
||||
| **S19a** 快速编辑内 @ 引用 | 资源卡「快速编辑」→ 提示词输入区「插入素材引用」或直接输入 `@` → 选素材 → 「修改」 | 快速编辑提示词输入区与聊天输入区**同一套**引用输入区:候选、插入动作、引用模型一致;提示词文本里的引用与聊天同字面量(`@显示名`),提交后该文本原样进入 `derive_local_project_resource` 的 `prompt` | #309 C4 / C10 | 快速编辑面板内存在 `aria-label="插入素材引用"` 与 `aria-label="快速编辑提示词"`;插入后编辑区出现 `[data-resource-reference-id]` chip;派生请求 `prompt` 等于 `文本@显示名` | 该输入区不渲染内置「AI 润色」(润色仍由面板的「AI 润色」承担,带资源编辑场景上下文与长度上限);多行输入区的 Enter 只换行、不提交;点 `@` 选择器里的候选不会被判成「点外部」而收起面板 |
|
||||
| **S20** 润色与发送前提醒 | ① 点「AI 润色」→ 看回填与「恢复原文」;② 直接发送(≥40 字)→ 处理提醒弹窗三条动作与「不再提醒」 | 润色只优化文本、不发送不提交;第一次成功润色落下原文快照、可重复润色、失败保留原文;未润色直接发送时弹「发送前提醒」 | L120(气泡对比度)、飞书 §12.1/12.2;#309 C8 | 润色按钮 `aria-label="AI 润色"` + `aria-busy`;出现「恢复原文」按钮;提醒弹窗为 portal `role="dialog"`、标题「发送前提醒」、动作 `关闭` / `使用原文提交` / `AI 润色` + checkbox `不再提醒`;偏好存 `localStorage['agc.chat.prompt-polish-reminder.disabled']` | 触发门禁:trim 后 ≥40 字、不以 `/` 开头、本轮未润色未确认、未勾「不再提醒」。计费不自建,走 server-rs `/api/llm/chat/completions` 与 `/api/llm/responses`;「1 泥点」这个数字源码中未硬编码 |
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| -------------------------- | ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **S19** @ 引用 | 输入区「@」→ 两个页签 + 筛选(功能分类 + 多标签)+ 搜索 + 多选 → 「添加到对话」 | 两页签**各自独立**的搜索与筛选状态(含各自一份标签库),**不影响**资源画布筛选;已选素材不因筛选或搜索隐藏而被取消;chip 整体插入 | L187、L420–426、L453–461;#309 C4 | `role="tablist"`(`aria-label="素材范围"`)内两个 `role="tab"`:`当前版本素材` / `全部画布素材`;输入框 aria-label 分别为 `搜索当前版本素材` / `搜索全部画布素材`;标签行是共享筛选条里 `aria-label="素材筛选标签"` 的分组,chip 用 `aria-pressed` 表达选中;chip = `span.resource-reference-chip[data-resource-reference-id]` | **chip 原子性有正式定义**:chip 是 Lexical `DecoratorNode`,纯文本表示固定占 **1 个字符** `U+FFFC` ⇒ 一次 Backspace 只能整体删掉一个 chip,不可能删半截;引用身份是稳定 `resourceId`,改名后按 id 刷新显示名。**多标签是 AND**(口径来自 `packages/shared` 的 `assetTagsMatchSelection`,标签取自 manifest `assets[].tags`),并与搜索词、功能分类三者叠加 |
|
||||
| **S19a** 快速编辑内 @ 引用 | 资源卡「快速编辑」→ 提示词输入区「插入素材引用」或直接输入 `@` → 选素材 → 「修改」 | 快速编辑提示词输入区与聊天输入区**同一套**引用输入区:候选、插入动作、引用模型一致;提示词文本里的引用与聊天同字面量(`@显示名`),提交后该文本原样进入 `derive_local_project_resource` 的 `prompt` | #309 C4 / C10 | 快速编辑面板内存在 `aria-label="插入素材引用"` 与 `aria-label="快速编辑提示词"`;插入后编辑区出现 `[data-resource-reference-id]` chip;派生请求 `prompt` 等于 `文本@显示名` | 该输入区不渲染内置「AI 润色」(润色仍由面板的「AI 润色」承担,带资源编辑场景上下文与长度上限);多行输入区的 Enter 只换行、不提交;点 `@` 选择器里的候选不会被判成「点外部」而收起面板 |
|
||||
| **S20** 润色与发送前提醒 | ① 点「AI 润色」→ 看回填与「恢复原文」;② 直接发送(≥40 字)→ 处理提醒弹窗三条动作与「不再提醒」 | 润色只优化文本、不发送不提交;第一次成功润色落下原文快照、可重复润色、失败保留原文;未润色直接发送时弹「发送前提醒」 | L120(气泡对比度)、飞书 §12.1/12.2;#309 C8 | 润色按钮 `aria-label="AI 润色"` + `aria-busy`;出现「恢复原文」按钮;提醒弹窗为 portal `role="dialog"`、标题「发送前提醒」、动作 `关闭` / `使用原文提交` / `AI 润色` + checkbox `不再提醒`;偏好存 `localStorage['agc.chat.prompt-polish-reminder.disabled']` | 触发门禁:trim 后 ≥40 字、不以 `/` 开头、本轮未润色未确认、未勾「不再提醒」。计费不自建,走 server-rs `/api/llm/chat/completions` 与 `/api/llm/responses`;「1 泥点」这个数字源码中未硬编码 |
|
||||
|
||||
### F 阶段 · 台账核对
|
||||
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| ---------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| **S21** 全程台账 | 不刷新、不重开项目,回看资源画布与布局 | 新增资源即时投影;两套布局(dependency / type)各自坐标独立、历史 `manuallyPlaced=true` 保留;搜索与详情开关不重置 viewport | L47–51、L262–265、L320–323、L327–329、L500 | 两份 sidecar 都在 `.agent/workbench/resource-layouts/{dependency,type}.json`;拖动超过 5px 阈值才提交一次 CAS,成功后 `revision` 递增;**存量项目「第一次打开」时读路径也会写回一次**(旧 `art` / `code` 分区坐标归并到新分区,`revision` +1),并给一次性提示条,条数含**无法对齐被跳过的坐标**(`data-resource-canvas-layout-*`),所以「`revision` 从 1 开始」不是异常 | 双窗口同 revision 的 CAS 冲突(L509)需开两个客户端窗口才可验;`A→B→A` 复用 epoch 的取消语义只能靠定向测试 |
|
||||
|
||||
## 4. 前置条件清单
|
||||
|
||||
| 项 | 结论 | 依据 |
|
||||
| --- | --- | --- |
|
||||
| **是否要重启客户端 / 重新构建** | dev 态 `npm run agc` 走 `devUrl`(默认 3080,运行时被覆盖为实际端口),前端改动走 Vite HMR、**不需要 build**;**Rust 侧改动必须重启客户端**;未提交改动若不在当前进程里,也必须重启一次客户端 | `apps/ai-game-creator-shell/src-tauri/tauri.conf.json:6-11`、`scripts/start-tauri-dev.mjs:26-44` |
|
||||
| **是否要重新构建产物** | 只有 `agc:build` / `ai-game-creator-shell:build` 才产出 `../dist`;若验的是打包后的客户端,改前端必须重新 build 并重装 | `tauri.conf.json` 的 `frontendDist: "../dist"` |
|
||||
| **是否要重启 api-server** | **服务端改动需要重启**。本地 `.env` / `.env.local` / `.env.secrets.local` 修改后必须重启 `api-server`;已在 `npm run dev` 终端里可直接敲 `rs api-server`;provider / worker / fallback 配置改动要按角色重启对应进程 | `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md:114/136/209/215/809` |
|
||||
| **启动命令** | 客户端:`npm run agc`(等价 `agc:dev` / `ai-game-creator-shell:dev`);只起前后端不起窗口:`npm run agc:serve`;后端:`npm run dev:api-server` | `package.json:20/155-159/169/22` |
|
||||
| **端口与健康检查** | api-server 默认 `127.0.0.1:8082`,健康检查 `GET /healthz`(先查 BgFilter worker `/readyz`);**端口会漂移,实际值以 `.app/dev-stack.json` 为准** | `scripts/dev.mjs:237`、`scripts/start-dev-stack.mjs:28-29/351-353` |
|
||||
| **登录与余额** | 需要登录;**不需要余额也能进工作台与资源画布**,消耗泥点的是付费生成与 Agent 请求 | `src/app/AuthenticatedClient.tsx:546/568-574`、`index.tsx:500` |
|
||||
| **测试项目** | 真机 `What do u wanna do kitten` 已有 61 assets / 5 versions、两份布局 sidecar 齐全,适合当**存量数据**验收对象(只读);要验删除与版本三分支时建议另建新项目 | 只读统计 |
|
||||
| **门禁命令** | `npm run ai-game-creator-shell:typecheck`、`npm run test -- apps/ai-game-creator-shell/tests`、`npm run check:encoding`、`git diff --check`;Rust 侧 `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml -- --test-threads=1` | `package.json:123/164/193-194/63` |
|
||||
| **build-gate 代理假红** | 本地带系统代理跑 `npm run build` 必假红:`NODE_USE_ENV_PROXY=1` 加 `HTTP_PROXY/https_proxy` 会触发 `[UNDICI-EHPA] Warning: EnvHttpProxyAgent is experimental`,不被 `ExperimentalWarning` 忽略规则放行。跑前清空 `NODE_USE_ENV_PROXY/HTTP_PROXY/http_proxy/HTTPS_PROXY/https_proxy/ALL_PROXY` | `scripts/build-gate.mjs:33-37/57-67` |
|
||||
| 项 | 结论 | 依据 |
|
||||
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| **是否要重启客户端 / 重新构建** | dev 态 `npm run agc` 走 `devUrl`(默认 3080,运行时被覆盖为实际端口),前端改动走 Vite HMR、**不需要 build**;**Rust 侧改动必须重启客户端**;未提交改动若不在当前进程里,也必须重启一次客户端 | `apps/ai-game-creator-shell/src-tauri/tauri.conf.json:6-11`、`scripts/start-tauri-dev.mjs:26-44` |
|
||||
| **是否要重新构建产物** | 只有 `agc:build` / `ai-game-creator-shell:build` 才产出 `../dist`;若验的是打包后的客户端,改前端必须重新 build 并重装 | `tauri.conf.json` 的 `frontendDist: "../dist"` |
|
||||
| **是否要重启 api-server** | **服务端改动需要重启**。本地 `.env` / `.env.local` / `.env.secrets.local` 修改后必须重启 `api-server`;已在 `npm run dev` 终端里可直接敲 `rs api-server`;provider / worker / fallback 配置改动要按角色重启对应进程 | `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md:114/136/209/215/809` |
|
||||
| **启动命令** | 客户端:`npm run agc`(等价 `agc:dev` / `ai-game-creator-shell:dev`);只起前后端不起窗口:`npm run agc:serve`;后端:`npm run dev:api-server` | `package.json:20/155-159/169/22` |
|
||||
| **端口与健康检查** | api-server 默认 `127.0.0.1:8082`,健康检查 `GET /healthz`(先查 BgFilter worker `/readyz`);**端口会漂移,实际值以 `.app/dev-stack.json` 为准** | `scripts/dev.mjs:237`、`scripts/start-dev-stack.mjs:28-29/351-353` |
|
||||
| **登录与余额** | 需要登录;**不需要余额也能进工作台与资源画布**,消耗泥点的是付费生成与 Agent 请求 | `src/app/AuthenticatedClient.tsx:546/568-574`、`index.tsx:500` |
|
||||
| **测试项目** | 真机 `What do u wanna do kitten` 已有 61 assets / 5 versions、两份布局 sidecar 齐全,适合当**存量数据**验收对象(只读);要验删除与版本三分支时建议另建新项目 | 只读统计 |
|
||||
| **门禁命令** | `npm run ai-game-creator-shell:typecheck`、`npm run test -- apps/ai-game-creator-shell/tests`、`npm run check:encoding`、`git diff --check`;Rust 侧 `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml -- --test-threads=1` | `package.json:123/164/193-194/63` |
|
||||
| **build-gate 代理假红** | 本地带系统代理跑 `npm run build` 必假红:`NODE_USE_ENV_PROXY=1` 加 `HTTP_PROXY/https_proxy` 会触发 `[UNDICI-EHPA] Warning: EnvHttpProxyAgent is experimental`,不被 `ExperimentalWarning` 忽略规则放行。跑前清空 `NODE_USE_ENV_PROXY/HTTP_PROXY/http_proxy/HTTPS_PROXY/https_proxy/ALL_PROXY` | `scripts/build-gate.mjs:33-37/57-67` |
|
||||
|
||||
## 5. 每步"失败怎么看"
|
||||
|
||||
@@ -129,13 +129,13 @@
|
||||
.reduce((m, e) => { const k = e.dataset.previewHasAlpha ?? '(无 alpha 判据)'; m[k] = (m[k] || 0) + 1; return m; }, {})
|
||||
```
|
||||
|
||||
| 现象 | 说明 | 第一眼做什么 |
|
||||
| --- | --- | --- |
|
||||
| 可读类型 + `idle` | 该卡**从未发出预览请求**(可见性门禁没挂上,或卡片在被裁切的容器里) | 先确认跑的是包含修复的新构建(见第 1 节),再核对 observer 补挂是否生效 |
|
||||
| `failed` + `data-preview-error` | 读取或解码真失败,读 `data-preview-error` 原文 | `retryable=false` 表示永久失败(超尺寸、损坏、类型不支持、不安全 SVG);可见性预取遇 failed 会直接放弃,不会自愈 |
|
||||
| `loaded` 但无主体节点 | 原生返回的 dataUrl 为空、或解码不出画面 | 这类**不报错**,只能靠脚本 ④ 抓 |
|
||||
| `placeholder` + `idle` | **不适用**(游戏代码、无法识别的二进制产物等),正常 | 不要当 bug 报 |
|
||||
| 卡面有棋盘格但 `data-preview-has-alpha` 缺失 | **这张图其实不透明**:棋盘格是图里画进去的(旧构建会额外再铺一层卡面棋盘格,两者叠在一起) | 脚本 ⑤ 分组确认;真透明卡才允许有 `'true'` |
|
||||
| 现象 | 说明 | 第一眼做什么 |
|
||||
| -------------------------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| 可读类型 + `idle` | 该卡**从未发出预览请求**(可见性门禁没挂上,或卡片在被裁切的容器里) | 先确认跑的是包含修复的新构建(见第 1 节),再核对 observer 补挂是否生效 |
|
||||
| `failed` + `data-preview-error` | 读取或解码真失败,读 `data-preview-error` 原文 | `retryable=false` 表示永久失败(超尺寸、损坏、类型不支持、不安全 SVG);可见性预取遇 failed 会直接放弃,不会自愈 |
|
||||
| `loaded` 但无主体节点 | 原生返回的 dataUrl 为空、或解码不出画面 | 这类**不报错**,只能靠脚本 ④ 抓 |
|
||||
| `placeholder` + `idle` | **不适用**(游戏代码、无法识别的二进制产物等),正常 | 不要当 bug 报 |
|
||||
| 卡面有棋盘格但 `data-preview-has-alpha` 缺失 | **这张图其实不透明**:棋盘格是图里画进去的(旧构建会额外再铺一层卡面棋盘格,两者叠在一起) | 脚本 ⑤ 分组确认;真透明卡才允许有 `'true'` |
|
||||
|
||||
**IPC 侧**:预览只走 4 条命令 `read_local_project_image_preview`、`read_local_project_media_preview`、`read_local_project_text_preview`、`cancel_local_project_resource_preview_scope`。媒体那条的 `category` **只接受 `art` / `audio`**,传栏目值会被原生拒绝,表现是卡片全空、没有缩略图。
|
||||
|
||||
@@ -153,26 +153,26 @@ window.__TAURI__.core.invoke = (cmd, args) => {
|
||||
|
||||
### 5.2 按步骤的排障索引
|
||||
|
||||
| 步骤 | 出问题时第一眼看什么 |
|
||||
| --- | --- |
|
||||
| S2 | `.game-workbench-stage` 的 `data-resource-view-state`;缺失说明误进 run。再调 `get_local_game_preview_status` 看活体,不要看 `manifest.preview` |
|
||||
| S4 | 对话后 `.agent/manifest.json` 的 `assets[]` 是否增长;底部 Agent Dock 的状态文案 |
|
||||
| S5 | 各 `[data-resource-book-category]` 下的卡片数量;某栏全空而 manifest 中明显有该类资产时,通常不是分区轴坏了 |
|
||||
| S6 | 5.1 的四条脚本 |
|
||||
| S7 | `[data-resource-book-view]` 是否为预期值,`.game-resource-book-scene-titlebar.is-active` 的 category 是否为预期栏目 |
|
||||
| S8 | `button[aria-label="搜索资源"]` 的 `aria-expanded`;`.game-resource-filter-panel` 是否真在 DOM(三字段同屏)。注意 jsdom 不校验可见性,只有真机截图或 `getBoundingClientRect` 才算数 |
|
||||
| S9 | `.is-selected` 数量与 `aria-pressed`;工具条容器 `role="toolbar"` 是否存在 |
|
||||
| S10 | 快速编辑失败文案;若为「当前素材类型不支持图片快速编辑」,说明源资源权威 `assetKind` 不在 17 项白名单里,这不是 V3 新缺陷 |
|
||||
| S11 | 生成入口在"没有 invoke 桥或项目未就绪"时**整体不渲染**,而不是渲染了但点了没反应 |
|
||||
| S12 | 保存后对比 `assets[n].tags` 与 `assets[n].category`,分类应逐字不变 |
|
||||
| S13 | 弹窗内 `role="alert"` 的错误文案;写盘失败时应看到文件已回滚原名 |
|
||||
| S14 | 是否有原生保存对话框;用户取消时是否停止后续批量保存 |
|
||||
| S15 | 弹窗是否出现 `被 N 个游戏版本使用`;不勾选时版本记录仍在(可读但悬空) |
|
||||
| S16 | `aria-label="当前版本:…"` 是否随切换更新;`[data-used-by-current-version="true"]` 集合是否变化 |
|
||||
| S17 | `get_local_game_preview_status` 返回的 `status/url/port`;`.agent/logs/preview.log` 的 `running` 行 |
|
||||
| S18 | 退出后重开项目是否落回资源总览;Rust 日志中的 `preview.gui_exit.stop_failed` |
|
||||
| S19 | chip 的 `data-resource-reference-id`;两页签 `role="tab"` 的 `aria-selected`;空态文案(`当前版本还没有绑定素材` / `当前项目还没有已登记素材` / `没有匹配的素材`) |
|
||||
| S20 | 弹窗标题「发送前提醒」;`localStorage['agc.chat.prompt-polish-reminder.disabled']` 的值;`润色中…` 或 `AI 润色失败,可重试` 状态行 |
|
||||
| 步骤 | 出问题时第一眼看什么 |
|
||||
| ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| S2 | `.game-workbench-stage` 的 `data-resource-view-state`;缺失说明误进 run。再调 `get_local_game_preview_status` 看活体,不要看 `manifest.preview` |
|
||||
| S4 | 对话后 `.agent/manifest.json` 的 `assets[]` 是否增长;底部 Agent Dock 的状态文案 |
|
||||
| S5 | 各 `[data-resource-book-category]` 下的卡片数量;某栏全空而 manifest 中明显有该类资产时,通常不是分区轴坏了 |
|
||||
| S6 | 5.1 的四条脚本 |
|
||||
| S7 | `[data-resource-book-view]` 是否为预期值,`.game-resource-book-scene-titlebar.is-active` 的 category 是否为预期栏目 |
|
||||
| S8 | `button[aria-label="搜索资源"]` 的 `aria-expanded`;`.game-resource-filter-panel` 是否真在 DOM(三字段同屏)。注意 jsdom 不校验可见性,只有真机截图或 `getBoundingClientRect` 才算数 |
|
||||
| S9 | `.is-selected` 数量与 `aria-pressed`;工具条容器 `role="toolbar"` 是否存在 |
|
||||
| S10 | 快速编辑失败文案;若为「当前素材类型不支持图片快速编辑」,说明源资源权威 `assetKind` 不在 17 项白名单里,这不是 V3 新缺陷 |
|
||||
| S11 | 生成入口在"没有 invoke 桥或项目未就绪"时**整体不渲染**,而不是渲染了但点了没反应 |
|
||||
| S12 | 保存后对比 `assets[n].tags` 与 `assets[n].category`,分类应逐字不变 |
|
||||
| S13 | 弹窗内 `role="alert"` 的错误文案;写盘失败时应看到文件已回滚原名 |
|
||||
| S14 | 是否有原生保存对话框;用户取消时是否停止后续批量保存 |
|
||||
| S15 | 弹窗是否出现 `被 N 个游戏版本使用`;不勾选时版本记录仍在(可读但悬空) |
|
||||
| S16 | `aria-label="当前版本:…"` 是否随切换更新;`[data-used-by-current-version="true"]` 集合是否变化 |
|
||||
| S17 | `get_local_game_preview_status` 返回的 `status/url/port`;`.agent/logs/preview.log` 的 `running` 行 |
|
||||
| S18 | 退出后重开项目是否落回资源总览;Rust 日志中的 `preview.gui_exit.stop_failed` |
|
||||
| S19 | chip 的 `data-resource-reference-id`;两页签 `role="tab"` 的 `aria-selected`;空态文案(`当前版本还没有绑定素材` / `当前项目还没有已登记素材` / `没有匹配的素材`) |
|
||||
| S20 | 弹窗标题「发送前提醒」;`localStorage['agc.chat.prompt-polish-reminder.disabled']` 的值;`润色中…` 或 `AI 润色失败,可重试` 状态行 |
|
||||
|
||||
**通用日志面**:WebView 的 `console.*` 会被镜像进 Rust `application.log`(标记 `__agcWebviewLogBridgeInstalled`),因此 F12 里打的日志事后也能在日志文件里复核。与本次验收最相关的两条是 `preview.gui_exit.stop_failed` 与 `[chat-markdown] render failed`。
|
||||
|
||||
@@ -185,32 +185,32 @@ api-server 是否本次重启:□ 是 □ 否
|
||||
测试项目:□ 真机 What do u wanna do kitten(只读) □ 新建项目 ____________________
|
||||
```
|
||||
|
||||
| 步骤 | 结果 | 备注(现象 / 证据 / 截图名) |
|
||||
| --- | --- | --- |
|
||||
| S1 建项并进工作台 | ☐ 过 ☐ 不过 | |
|
||||
| S2 落 `resource-overview` | ☐ 过 ☐ 不过 | |
|
||||
| S3 项目页面板(可选) | ☐ 过 ☐ 不过 ☐ 跳过 | |
|
||||
| S4 对话生成 → manifest 登记 | ☐ 过 ☐ 不过 | assets:___ → ___ |
|
||||
| S5 7 栏目归属正确 | ☐ 过 ☐ 不过 | 各栏计数:______ |
|
||||
| S6 卡片预览台账(idle / failed / 空) | ☐ 过 ☐ 不过 | idle:__ failed:__ 空:__ |
|
||||
| S7 栏目导航(含「所有资源」/ 下一页 / 空栏目) | ☐ 过 ☐ 不过 | |
|
||||
| S8 单一筛选浮层(三字段)+ 不清条件 | ☐ 过 ☐ 不过 | |
|
||||
| S9 选择 / 多选 / 框选 / 工具条 | ☐ 过 ☐ 不过 | 实际渲染的动作:______ |
|
||||
| S10 图片快速编辑(新素材 + 血缘) | ☐ 过 ☐ 不过 | 是否 400:☐ 否 ☐ 是(原因:____) |
|
||||
| S11 生成入口(视频 / 音效 / 背景音乐) | ☐ 过 ☐ 不过 ☐ 未验 | |
|
||||
| S12 编辑标签(只改 tags) | ☐ 过 ☐ 不过 | category 是否原样:☐ 是 ☐ 否 |
|
||||
| S13 重命名(id 不变) | ☐ 过 ☐ 不过 | |
|
||||
| S14 下载(另存为) | ☐ 过 ☐ 不过 | |
|
||||
| S15 删除三分支 | ☐ 无引用 ☐ 引用未勾选 ☐ 勾选连带 | |
|
||||
| S16 版本切换 + 当前使用高亮 + @ 同步 | ☐ 过 ☐ 不过 | |
|
||||
| S17 播放 → 运行视图 + 本地预览 | ☐ 过 ☐ 不过 | url:______________ |
|
||||
| S18 停止预览 / 退出收尾对齐 stopped | ☐ 过 ☐ 不过 | |
|
||||
| S19 @ 引用两页签 + 独立筛选 + chip 原子性 | ☐ 过 ☐ 不过 | |
|
||||
| S19a @ 面板标签筛选(多标签 AND,叠加关键字与功能分类) | ☐ 过 ☐ 不过 | |
|
||||
| S19b 快速编辑提示词内 @ 引用(同聊天引用模型与字面量) | ☐ 过 ☐ 不过 | |
|
||||
| S20 润色 + 发送前提醒 + 不再提醒 | ☐ 过 ☐ 不过 | |
|
||||
| S21 台账(即时同步 / 双布局 / 无溢出) | ☐ 过 ☐ 不过 | 1280×800 ☐ 1280×720 ☐ |
|
||||
| 门禁:typecheck / AGC tests / check:encoding / git diff --check | ☐ 过 ☐ 不过 | |
|
||||
| 步骤 | 结果 | 备注(现象 / 证据 / 截图名) |
|
||||
| --------------------------------------------------------------- | -------------------------------- | ------------------------------------- |
|
||||
| S1 建项并进工作台 | ☐ 过 ☐ 不过 | |
|
||||
| S2 落 `resource-overview` | ☐ 过 ☐ 不过 | |
|
||||
| S3 项目页面板(可选) | ☐ 过 ☐ 不过 ☐ 跳过 | |
|
||||
| S4 对话生成 → manifest 登记 | ☐ 过 ☐ 不过 | assets:**_ → _** |
|
||||
| S5 7 栏目归属正确 | ☐ 过 ☐ 不过 | 各栏计数:**\_\_** |
|
||||
| S6 卡片预览台账(idle / failed / 空) | ☐ 过 ☐ 不过 | idle:** failed:** 空:\_\_ |
|
||||
| S7 栏目导航(含「所有资源」/ 下一页 / 空栏目) | ☐ 过 ☐ 不过 | |
|
||||
| S8 单一筛选浮层(三字段)+ 不清条件 | ☐ 过 ☐ 不过 | |
|
||||
| S9 选择 / 多选 / 框选 / 工具条 | ☐ 过 ☐ 不过 | 实际渲染的动作:**\_\_** |
|
||||
| S10 图片快速编辑(新素材 + 血缘) | ☐ 过 ☐ 不过 | 是否 400:☐ 否 ☐ 是(原因:\_\_\_\_) |
|
||||
| S11 生成入口(视频 / 音效 / 背景音乐) | ☐ 过 ☐ 不过 ☐ 未验 | |
|
||||
| S12 编辑标签(只改 tags) | ☐ 过 ☐ 不过 | category 是否原样:☐ 是 ☐ 否 |
|
||||
| S13 重命名(id 不变) | ☐ 过 ☐ 不过 | |
|
||||
| S14 下载(另存为) | ☐ 过 ☐ 不过 | |
|
||||
| S15 删除三分支 | ☐ 无引用 ☐ 引用未勾选 ☐ 勾选连带 | |
|
||||
| S16 版本切换 + 当前使用高亮 + @ 同步 | ☐ 过 ☐ 不过 | |
|
||||
| S17 播放 → 运行视图 + 本地预览 | ☐ 过 ☐ 不过 | url:******\_\_****** |
|
||||
| S18 停止预览 / 退出收尾对齐 stopped | ☐ 过 ☐ 不过 | |
|
||||
| S19 @ 引用两页签 + 独立筛选 + chip 原子性 | ☐ 过 ☐ 不过 | |
|
||||
| S19a @ 面板标签筛选(多标签 AND,叠加关键字与功能分类) | ☐ 过 ☐ 不过 | |
|
||||
| S19b 快速编辑提示词内 @ 引用(同聊天引用模型与字面量) | ☐ 过 ☐ 不过 | |
|
||||
| S20 润色 + 发送前提醒 + 不再提醒 | ☐ 过 ☐ 不过 | |
|
||||
| S21 台账(即时同步 / 双布局 / 无溢出) | ☐ 过 ☐ 不过 | 1280×800 ☐ 1280×720 ☐ |
|
||||
| 门禁:typecheck / AGC tests / check:encoding / git diff --check | ☐ 过 ☐ 不过 | |
|
||||
|
||||
## 7. 风险与盲区
|
||||
|
||||
@@ -218,30 +218,30 @@ api-server 是否本次重启:□ 是 □ 否
|
||||
|
||||
需求源头文档《GameAgent客户端画布与资源工作台 V3.0》(飞书 wiki 文档,文档 token 以 `N4Kiw…` 开头)的文本层共 **796 行文本、5 段视频、2 张图片**。以下条款**只能靠视频或截图验收**,文本层只有"标题式"描述,缺可判定细节:
|
||||
|
||||
| # | 位置 | 媒体 | 文本只到哪 | 缺什么(无法从文本验收的部分) |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | §3.1 默认视图 | **截图**(图注「资源管理视图」) | "默认进入'全部资源'视图……功能画布分类:UI 与交互、角色与对象、场景与环境、音频、文档、待归类" | 总览的**版式**:缩略卡片排布、层叠张数、缩略图尺寸与间距、标题栏位置 |
|
||||
| 2 | §3.2 筛选 | **视频** `20260907-0309-29.2729736.mp4`("增加自定义标签") | "点击已有标签 / 输入新标签 / 保存后同步进全局标签库" | 面板**长什么样**、pill 排布、输入框提示文案、保存按钮位置 |
|
||||
| 3 | §3.2 筛选 | **视频** `20260907-0321-46.9223141.mp4`("查看素材标签") | "标签不默认显示在卡片上,需在'编辑标签'中查看" | 查看入口与展示形态 |
|
||||
| 4 | §3.2 筛选 | **视频** `20260907-0322-33.1365605.mp4`("按照标签筛选素材") | 文本给了筛选逻辑(条件即时生效 / 清空恢复 / 不影响归属) | 筛选面板的交互与视觉:多标签是 AND 还是 OR 的**界面表达**、标签汇总列表样式 |
|
||||
| 5 | §3.2 筛选 | **视频** `20260907-0323-34.3948031.mp4`("删除标签") | "删除标签时从所有素材和筛选条件中移除;'管理全部标签'里可删" | 「管理全部标签」这个入口 / 面板本身**只存在于视频**,AGC 侧未找到对应实现 |
|
||||
| 6 | §10.1 / §10.2 @ 引用 | **视频** `20260906-0812-23.9790394.mp4` + **截图**(提示框"请把@开始按钮并保持原位置",截图注明"参考 loveart") | "资源卡工具栏提供 @引用 工具";"所有@后产生的标签都在对话框当中,用户可以在标签前后编辑上下文" | chip 的**外观**(是否带图标 / 缩略图 / 颜色)、插入后光标落点、与 Lovart 的对照样式 |
|
||||
| 7 | PRD L139(项目管理页) | 文档**明确要求**提交截图 + 视频 | "截图必须使用含 Web、Godot 和无效状态的 populated fixture;视频必须演示搜索、清除、行尾菜单及可观察的项目操作结果" | 这条本身就是"必须交视频"的要求,不是可由文本判定的功能条款 |
|
||||
| # | 位置 | 媒体 | 文本只到哪 | 缺什么(无法从文本验收的部分) |
|
||||
| --- | ---------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
|
||||
| 1 | §3.1 默认视图 | **截图**(图注「资源管理视图」) | "默认进入'全部资源'视图……功能画布分类:UI 与交互、角色与对象、场景与环境、音频、文档、待归类" | 总览的**版式**:缩略卡片排布、层叠张数、缩略图尺寸与间距、标题栏位置 |
|
||||
| 2 | §3.2 筛选 | **视频** `20260907-0309-29.2729736.mp4`("增加自定义标签") | "点击已有标签 / 输入新标签 / 保存后同步进全局标签库" | 面板**长什么样**、pill 排布、输入框提示文案、保存按钮位置 |
|
||||
| 3 | §3.2 筛选 | **视频** `20260907-0321-46.9223141.mp4`("查看素材标签") | "标签不默认显示在卡片上,需在'编辑标签'中查看" | 查看入口与展示形态 |
|
||||
| 4 | §3.2 筛选 | **视频** `20260907-0322-33.1365605.mp4`("按照标签筛选素材") | 文本给了筛选逻辑(条件即时生效 / 清空恢复 / 不影响归属) | 筛选面板的交互与视觉:多标签是 AND 还是 OR 的**界面表达**、标签汇总列表样式 |
|
||||
| 5 | §3.2 筛选 | **视频** `20260907-0323-34.3948031.mp4`("删除标签") | "删除标签时从所有素材和筛选条件中移除;'管理全部标签'里可删" | 「管理全部标签」这个入口 / 面板本身**只存在于视频**,AGC 侧未找到对应实现 |
|
||||
| 6 | §10.1 / §10.2 @ 引用 | **视频** `20260906-0812-23.9790394.mp4` + **截图**(提示框"请把@开始按钮并保持原位置",截图注明"参考 loveart") | "资源卡工具栏提供 @引用 工具";"所有@后产生的标签都在对话框当中,用户可以在标签前后编辑上下文" | chip 的**外观**(是否带图标 / 缩略图 / 颜色)、插入后光标落点、与 Lovart 的对照样式 |
|
||||
| 7 | PRD L139(项目管理页) | 文档**明确要求**提交截图 + 视频 | "截图必须使用含 Web、Godot 和无效状态的 populated fixture;视频必须演示搜索、清除、行尾菜单及可观察的项目操作结果" | 这条本身就是"必须交视频"的要求,不是可由文本判定的功能条款 |
|
||||
|
||||
**补齐方式**:① 由产品口述 2–3 句关键视觉约束后改写为判据;② 用 `lark-cli docs +media-download` 取出这 5 段视频与 2 张图并抽取静帧后逐帧写判据;③ 降级为"人工目视,不计入自动化判据",记录表里只打勾。
|
||||
|
||||
### 7.2 本地环境无法验证 / 需要额外条件的项
|
||||
|
||||
| 项 | 为什么本地不可验 | 建议 |
|
||||
| --- | --- | --- |
|
||||
| 双窗口布局 CAS 冲突(L509) | 需同一项目同时开两个客户端窗口、基于同一 revision 同时写入 | 标为"需专项构造",或只跑 `resourceCanvasLayoutContract` 定向测试 |
|
||||
| 4096 资源链式 fixture 性能(L522) | 需构造大规模 fixture | 交给定向测试,不进人工主线 |
|
||||
| `1280×720` / `1280×800` 视觉验收(L512、L577) | 必须真实窗口 + 截图或测量 `document/body` 的 `client` 与 `scroll` | 每条视觉结论附截图;jsdom 不做布局,vitest 中的可见性断言是假守卫 |
|
||||
| Windows 平台性断言 | 非 Linux 平台本地预览用**随机临时端口**;端口池耗尽回退、`GENARRATIVE_DEV_PORT_RANGE`、命令沙箱模块**只在 Linux 生效**;文件锁 Unix 用 `flock`、Windows 不共享句柄 | 涉及这些的判据不能反推平台行为。反之"delete-pending 等行为级 Windows 用例 CI 没有 runner,只在 Windows 本地跑"——本地反而能验到 CI 验不到的 |
|
||||
| `build-gate` 代理假红 | 见第 4 节 | 跑前清代理变量,或用 `build:raw` |
|
||||
| worktree 专属假红 | worktree 里 `check-repository-ci.sh` 第一步即"比较基线不可用";jsdom 拿不到 fit key | 改用 Windows git 逐条等价执行;这两类红不当缺陷 |
|
||||
| 既有失败用例 | 技术方案记录过 AGC 前端 5 条既有失败(C3 交互改造后待更新用例);Rust 并行跑 `project::` 约 10 条环境依赖失败,仓库口径是 `--test-threads=1` | 预置"已知红名单",避免重复排查 |
|
||||
| F12 看不到 Rust → api-server 的 HTTP | 见 5.1 更正 | 用 api-server 日志或 `.agent/logs` |
|
||||
| 项 | 为什么本地不可验 | 建议 |
|
||||
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| 双窗口布局 CAS 冲突(L509) | 需同一项目同时开两个客户端窗口、基于同一 revision 同时写入 | 标为"需专项构造",或只跑 `resourceCanvasLayoutContract` 定向测试 |
|
||||
| 4096 资源链式 fixture 性能(L522) | 需构造大规模 fixture | 交给定向测试,不进人工主线 |
|
||||
| `1280×720` / `1280×800` 视觉验收(L512、L577) | 必须真实窗口 + 截图或测量 `document/body` 的 `client` 与 `scroll` | 每条视觉结论附截图;jsdom 不做布局,vitest 中的可见性断言是假守卫 |
|
||||
| Windows 平台性断言 | 非 Linux 平台本地预览用**随机临时端口**;端口池耗尽回退、`GENARRATIVE_DEV_PORT_RANGE`、命令沙箱模块**只在 Linux 生效**;文件锁 Unix 用 `flock`、Windows 不共享句柄 | 涉及这些的判据不能反推平台行为。反之"delete-pending 等行为级 Windows 用例 CI 没有 runner,只在 Windows 本地跑"——本地反而能验到 CI 验不到的 |
|
||||
| `build-gate` 代理假红 | 见第 4 节 | 跑前清代理变量,或用 `build:raw` |
|
||||
| worktree 专属假红 | worktree 里 `check-repository-ci.sh` 第一步即"比较基线不可用";jsdom 拿不到 fit key | 改用 Windows git 逐条等价执行;这两类红不当缺陷 |
|
||||
| 既有失败用例 | 技术方案记录过 AGC 前端 5 条既有失败(C3 交互改造后待更新用例);Rust 并行跑 `project::` 约 10 条环境依赖失败,仓库口径是 `--test-threads=1` | 预置"已知红名单",避免重复排查 |
|
||||
| F12 看不到 Rust → api-server 的 HTTP | 见 5.1 更正 | 用 api-server 日志或 `.agent/logs` |
|
||||
|
||||
### 7.3 已知未做 / 已取消(不要报成缺陷)
|
||||
|
||||
@@ -252,7 +252,7 @@ api-server 是否本次重启:□ 是 □ 否
|
||||
5. **「首轮进度投影」无编码级判据**,可见性由底部 Agent 状态栏承载。
|
||||
6. **浮出工具条的视觉位置未做真机核验**(jsdom 不执行 Web Animations,只覆盖结构与 class)。
|
||||
7. **重命名不改游戏源码中的旧 `assets/<name>` 引用**。
|
||||
8. **C9 参考图弹窗在 AGC 侧入口未确认**:全仓检索 `添加参考图` 只命中共享 `src/components/image-editor/*`,AGC 的 `ResourceCanvasGenerationPanelView` 未引用它,可能处于"组件已实现、AGC 入口未接"状态。建议真机点一次生成 / 编辑面板确认;若无入口,C9 不计入本轮主线。**注**:资源替换复用同一个 `ImageCanvasProjectAssetPickerDialog`,它在 AGC 侧是首次使用(同一个弹窗、不同的 opt-in 参数)。
|
||||
8. **C9 参考图弹窗在 AGC 侧入口未确认**:全仓检索 `添加参考图` 只命中共享 `src/components/image-editor/*`,AGC 的 `ResourceCanvasGenerationPanelView` 未引用它,可能处于"组件已实现、AGC 入口未接"状态。建议真机点一次生成 / 编辑面板确认;若无入口,C9 不计入本轮主线。**注**:资源替换复用同一个 `ImageCanvasProjectAssetPickerDialog`,它在 AGC 侧是首次使用(2026-09-21 起走该组件的 `nonModal` 非模态形态:不铺遮罩、面板锚在画布容器右上角,见 S15a)。
|
||||
9. **运行视图存在「点选素材」按钮**,但 #309 的"不做"清单包含运行画面点选,口径冲突待定。
|
||||
10. **替换成功后运行画面不会立刻变**:按 C7 与本轮口径只做记录层 + UI 层,可见变化是资源卡"当前使用"高亮移到替换素材与 `@` 面板"当前版本素材"更新;**不会新增版本卡、也不切换版本**,这不等于替换失败。
|
||||
11. **替换候选弹窗不加载缩略图**:AGC 的素材预览要经带 scope 的原生读取器拿 Blob URL,弹窗内没有同步 `src`,因此候选行只渲染类型占位(不给 `<img>` 喂空串、不挂破图)。
|
||||
@@ -261,15 +261,15 @@ api-server 是否本次重启:□ 是 □ 否
|
||||
|
||||
### 7.4 文档与代码的偏差(需明确按哪个判)
|
||||
|
||||
| 偏差 | 文档表述 | 代码实际 | 建议 |
|
||||
| --- | --- | --- | --- |
|
||||
| 资源卡「打开详情」 | PRD L61–62 要求"打开详情"与"播放 / 暂停"是可分别键盘聚焦的同级按钮 | 不存在该按钮(测试显式断言为 `null`);真正入口是 `选中资源:<栏目> <名>`,播放按钮是 `播放/暂停 <名>`;**只读信息浮层已回归**(2026-09-11 工具条「信息」动作) | 按 #309 C3(取消资源详情面板与全屏编辑路由)判,**C3 取消的仍只是可编辑详情面板与全屏编辑路由**;PRD L61–62 建议同步修订 |
|
||||
| 运行入口不可用文案 | PRD L165 显示"当前无可运行版本",但仍允许点击 | 源码中无此字符串;实现为「首个可运行原型尚未完成,运行视图暂不可用」;且播放按钮 `disabled`,只有"运行"tab 不 disabled、用 `data-unavailable` 表达 | 建议按"tab 可点 + 播放按钮置灰"判,并把 PRD L165 文案对齐实现 |
|
||||
| 画布生成入口 | #309「画布生成入口未接」 | 已接两条:浮层入口只做视频;音频与图片类在栏目画布底部工具栏(见 S11a / PRD §3.10) | 按代码与当前产品口径判,并更新 #309 进度段 |
|
||||
| 分类手设入口 | 旧版 PRD「用户可在分类与标签面板手动设置 category」 | 已移除;面板只编辑 tags(已随 `6bdc8bbd9` 入库) | 按新口径判 |
|
||||
| 真机 manifest 分类分布 | — | 落盘 `unclassified 57 / ui-interaction 3 / scene 1` | **读时自愈**会把 kind 可明确分类的资产显示到正确栏目,判定必须看 UI 栏目计数,不能看 manifest 文件,否则必然误报"分类不生效" |
|
||||
| 偏差 | 文档表述 | 代码实际 | 建议 |
|
||||
| ---------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 资源卡「打开详情」 | PRD L61–62 要求"打开详情"与"播放 / 暂停"是可分别键盘聚焦的同级按钮 | 不存在该按钮(测试显式断言为 `null`);真正入口是 `选中资源:<栏目> <名>`,播放按钮是 `播放/暂停 <名>`;**只读信息浮层已回归**(2026-09-11 工具条「信息」动作) | 按 #309 C3(取消资源详情面板与全屏编辑路由)判,**C3 取消的仍只是可编辑详情面板与全屏编辑路由**;PRD L61–62 建议同步修订 |
|
||||
| 运行入口不可用文案 | PRD L165 显示"当前无可运行版本",但仍允许点击 | 源码中无此字符串;实现为「首个可运行原型尚未完成,运行视图暂不可用」;且播放按钮 `disabled`,只有"运行"tab 不 disabled、用 `data-unavailable` 表达 | 建议按"tab 可点 + 播放按钮置灰"判,并把 PRD L165 文案对齐实现 |
|
||||
| 画布生成入口 | #309「画布生成入口未接」 | 已接两条:浮层入口只做视频;音频与图片类在栏目画布底部工具栏(见 S11a / PRD §3.10) | 按代码与当前产品口径判,并更新 #309 进度段 |
|
||||
| 分类手设入口 | 旧版 PRD「用户可在分类与标签面板手动设置 category」 | 已移除;面板只编辑 tags(已随 `6bdc8bbd9` 入库) | 按新口径判 |
|
||||
| 真机 manifest 分类分布 | — | 落盘 `unclassified 57 / ui-interaction 3 / scene 1` | **读时自愈**会把 kind 可明确分类的资产显示到正确栏目,判定必须看 UI 栏目计数,不能看 manifest 文件,否则必然误报"分类不生效" |
|
||||
|
||||
## 8. 验收边界
|
||||
|
||||
- 本用例覆盖:资源总览 / 栏目分页画布、资源卡预览与本地受控读取、资源卡选择与多选、快速编辑派生(含提示词内 @ 引用)、画布生成入口(浮层视频 + 栏目工具栏图片类与音频)、编辑标签、重命名、下载、删除三分支、版本切换与当前使用高亮、**资源替换(入口放行判据 / 候选禁用与原因 / 格式提示 / 写入载荷 / 拒绝零副作用 / 成功后不切版本不重载预览 / 弹窗与画布点选两条入口同一条写入链路)**、聊天 @ 引用(两页签 + 功能分类 + 多标签筛选 + 搜索)与原子 chip、AI 润色与发送前提醒、本地预览启动与退出收尾、两份布局 sidecar 台账。
|
||||
- 本用例覆盖:资源总览 / 栏目分页画布、资源卡预览与本地受控读取、资源卡选择与多选、快速编辑派生(含提示词内 @ 引用)、画布生成入口(浮层视频 + 栏目工具栏图片类与音频)、编辑标签、重命名、下载、删除三分支、版本切换与当前使用高亮、**资源替换(入口放行判据 / 候选禁用与原因 / 格式提示 / 写入载荷 / 拒绝零副作用 / 成功后不切版本不重载预览 / 非模态面板 + 画布点选目标同一条写入链路)**、聊天 @ 引用(两页签 + 功能分类 + 多标签筛选 + 搜索)与原子 chip、AI 润色与发送前提醒、本地预览启动与退出收尾、两份布局 sidecar 台账。
|
||||
- 本用例不覆盖(另走专项或定向测试):双窗口 CAS 冲突、大规模 fixture 性能、素材创作无限画布阶段一至五的草稿 / 事务 / 恢复矩阵(见 `【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md` 与配套专题)、UI 编辑器子路由、主站图片编辑器回归(见 PRD §7.7)。资源替换的后端矩阵(版本绑定改写放行的六条不变式与两组互斥、绑定改写两条路径、硬门禁与提示、CAS、四条拒绝路径、读时自愈口径、审计留痕)见 `apps/ai-game-creator-shell/src-tauri/src/project/manifest/version_binding_rewrite_tests.rs` 与 `apps/ai-game-creator-shell/src-tauri/src/tests/version_resource_replacement.rs`,前端矩阵见 `apps/ai-game-creator-shell/tests/resourceVersionReplacement*.test.ts(x)`。
|
||||
|
||||
Reference in New Issue
Block a user