Merge remote-tracking branch 'origin/master' into feat/ui-editor-v3
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 3m0s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 3m4s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 3m7s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 3m9s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m41s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m32s
Project CI / Frontend tests (pull_request) Failing after 4m47s
Project CI / Repository checks (pull_request) Failing after 4m39s
Project CI / Native shell tests (pull_request) Successful in 7m14s
Project CI / Backend tests (pull_request) Successful in 8m12s
Project CI / AI game creator shell web tests (pull_request) Failing after 2m54s

# Conflicts:
#	docs/project-memory/shared-memory/pitfalls.md
This commit is contained in:
2026-09-16 14:57:26 +08:00
311 changed files with 32093 additions and 5008 deletions
+2
View File
@@ -34,6 +34,7 @@
- [AGC 客户端稳定版生命周期大切换](./【技术方案】AGC客户端稳定版生命周期大切换-2026-09-14.md):统一 operation、认证/Runner、项目入口、本地恢复和 dev-stack 身份边界。
- [策划会话 Runtime V2 接入与旧链路退役方案](./technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md):新单 Agent 策划会话、GDD 策略、未来 MCP/Skill 兼容插槽、阶段任务与退役验收合同。
- [DirectProject Codex 原始历史与异常恢复](<./technical/【技术方案】DirectProject Codex原始历史与异常恢复-2026-09-04.md>):原始 Responses item 持久化、线程注入与异常回合收尾。
- [GameAgent 对话工具调用卡片](./technical/【技术方案】GameAgent对话工具调用卡片-2026-09-14.md):把右侧对话里的执行命令 / 写文件投影成 Codex 风格可折叠卡片,含采集、独立历史文件、事件字段与回读契约。
- [DirectProject 客户端 Skill 与 MCP 扩展导入方案](./technical/【技术方案】DirectProject客户端Skill与MCP扩展导入方案-2026-08-31.md):客户端扩展导入、按独立 Skill/MCP 拆分、命名、启用和启动时注入边界。
- [AGC 通用插件宿主与编辑器适配](./technical/【技术方案】AGC通用插件宿主与编辑器适配-2026-09-09.md):通用插件宿主、SDK、权限审计、UI 挂载和 Cocos 编辑器适配边界。
- [AGC Cocos Creator 编辑器桥接模块](<./technical/【技术方案】AGC Cocos Creator 编辑器桥接模块-2026-09-09.md>):独立 crate、feature 开关、目标校验与 Windows 注入边界。
@@ -45,6 +46,7 @@
- [立项策划 AgentFast GDD](<./technical/【技术方案】立项策划AgentFast GDD-2026-08-10.md>):旧 `project-supervisor-plan` / `project-planning` 历史会话的入口、审批和恢复合同;V2 切换时未完成旧会话强制失败。
- [GameAgent 资源自由画板与快速编辑](./technical/【技术方案】GameAgent资源自由画板与快速编辑-2026-08-20.md)
- [AGC 栏目画布底部工具栏入口矩阵](./technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md):栏目画布底部工具栏的入口矩阵、可用条件与验收口径。
- [AGC 资源工作台三处交互收口改动前对照图](./technical/assets/agc-resource-workbench-ui-before-20260914/README.md):任务侧栏两个关闭入口、左侧贴边折叠把手、顶部播放按钮居中悬浮三张改动前截图与问题说明。
- [AGC 资源派生与非破坏性编辑合同](./technical/【技术方案】AGC资源派生与非破坏性编辑合同-2026-09-09.md):AGC 全类型现有资源非破坏性编辑的权威合同,约束资源派生、替换与写回边界。
- [AGC 聊天素材引用](./【功能说明】AGC聊天素材引用-2026-09-08.md):聊天输入框 @ 引用项目素材的入口、引用模型与「当前版本素材」口径。
- [AGC 聊天 AI 润色与发送前提醒](./【功能说明】AGC聊天AI润色与发送前提醒-2026-09-10.md):提示词润色与发送前提醒的交互、失败与取消口径。
@@ -3370,16 +3370,32 @@
"maxLength": 200
}
},
"sliceLayout": {
"sliceMode": {
"type": "string",
"deprecated": true,
"description": "历史兼容字段,新的调用请使用 sliceCount"
"enum": [
"connected-components",
"grid"
],
"default": "connected-components",
"description": "图集切分模式。connected-components 按透明像素 alpha 连通域识别独立素材;grid 按用户提供的 gridX/gridY 划分网格槽。省略时使用 connected-components。"
},
"gridX": {
"type": "integer",
"minimum": 1,
"maximum": 32,
"description": "grid 模式的横向网格数量。"
},
"gridY": {
"type": "integer",
"minimum": 1,
"maximum": 32,
"description": "grid 模式的纵向网格数量。"
},
"sliceCount": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"description": "可选的目标切片数量;省略时按图像内容自动识别。"
"description": "connected-components 模式下可选的目标切片数量;省略时按图像内容自动识别。grid 模式的切片数量由 gridX×gridY 决定。"
},
"screenColor": {
"type": ["string", "null"],
@@ -3603,15 +3619,28 @@
},
"iconImageSrcs": {
"type": "array",
"description": "识别图集中有效 alpha 连通域并持久化的独立素材,按视觉阅读顺序命名为“素材 N”;可通过 sliceCount 指定目标数量。",
"description": "按 sliceMode 识别或裁切并持久化的独立素材,按视觉阅读顺序命名为“素材 N”;connected-components 模式可通过 sliceCount 指定目标数量。",
"items": {
"$ref": "#/components/schemas/EditorIconSpritesheetIconResult"
}
},
"sliceLayout": {
"sliceMode": {
"type": "string",
"deprecated": true,
"description": "历史兼容字段。"
"enum": [
"connected-components",
"grid"
],
"description": "实际采用的图集切分模式。"
},
"gridX": {
"type": "integer",
"minimum": 1,
"maximum": 32
},
"gridY": {
"type": "integer",
"minimum": 1,
"maximum": 32
},
"sliceCount": {
"type": "integer",
@@ -3628,7 +3657,7 @@
"type": "null"
}
],
"description": "可信透明图集已成功持久化,但全连通域自动拆分未完成时返回;此时 iconImageSrcs 为空,调用方仍应使用整张图集。原始连通域、输出数量或 CPU 预算超限不会产生切片 PUT、资源或画布切片。透明处理、Alpha/尺寸恢复、provider 原图修复性回读或透明图完整解码失败时走 provider 原图 source-onlysliceWarning 为 null。"
"description": "可信透明图集已成功持久化,但所选 sliceMode 的自动拆分未完成时返回;此时 iconImageSrcs 为空,调用方仍应使用整张图集。原始连通域、输出数量、网格裁切或 CPU 预算超限不会产生切片 PUT、资源或画布切片。透明处理、Alpha/尺寸恢复、provider 原图修复性回读或透明图完整解码失败时走 provider 原图 source-onlysliceWarning 为 null。"
},
"prompt": {
"type": "string"
@@ -1,6 +1,6 @@
# AI 游戏创作项目开发工作台 PRD
更新时间:`2026-09-13`(新增功能画布底部工具栏入口矩阵 §3.10 / §7.9右侧 Supervisor 对话气泡、可访问对比度与过程卡布局收口,以及资源卡预览、分区布局、非破坏性资源编辑、资源替换与 Godot 双根合同保持不变)
更新时间:`2026-09-14`同步功能画布图片类生成后台化的当前事实:入口 IPC 改为 `start_local_project_asset_generation` + 项目内任务账本 + 本地排队 + 非模态「生成任务」面板,见 §3.10 / §7.92026-09-13 新增功能画布底部工具栏入口矩阵 §3.10 / §7.9,以及右侧 Supervisor 对话气泡、可访问对比度与过程卡布局收口,资源卡预览、分区布局、非破坏性资源编辑、资源替换与 Godot 双根合同保持不变)
## 1. 产品定位
@@ -111,6 +111,12 @@
2026-08-23:项目开发工作台取消顶部账户资产预留空间,资源管理主视窗与 Agent 对话从窗口顶端铺开;泥点余额 / 充值入口复用既有账户组件并放置在 Agent 对话标题栏右上角,对话标题在该视窗顶部居中。
2026-09-16:项目内工具栏增加“打开项目目录”入口,资源管理与运行页签均可使用;入口复用现有本地项目目录打开命令,不新增项目状态或文件访问链路。
2026-09-16:客户端标题栏账户资产条在传入兑换能力时直接显示“兑换码”按钮;兑换弹窗和账号生命周期校验继续复用现有实现。
2026-09-16:客户端首页移除“做素材”创作类型,项目工作台顶部移除“生成素材”按钮;素材生成继续由项目内实际资源工作流入口承载。
- 项目工作台继续保留左侧平台导航、中央主视窗、右侧 Project Supervisor 和底部专业 Agent 状态栏四区结构;主站图片编辑器只作为视觉语言和共享画布组件的事实源,不把其素材库侧栏、账号业务或云端项目外壳整体搬入客户端。
- 平台主题事实源固定为 `packages/shared/src/theme.css`。画布通用 chrome 固定落在 `@genarrative/image-canvas-react`,主站与 Tauri 必须直接 import 同一组件和作用域样式;客户端不得复制 `src/components/image-editor/`,也不得导入主站完整 `src/index.css`
- 第一批共享 chrome 固定覆盖画布动作按钮、工具栏、工具分组和分隔符。按钮的默认、悬停、键盘焦点、选中、禁用和主次色语义由共享层表达;宿主只提供图标、文案、事件与业务禁用条件。
@@ -121,7 +127,7 @@
- 资源总览的“资源依赖 / 资源类型”视图切换使用连通的分段按钮组,相邻选项共享边界并保持唯一选中语义。每个分段都必须有清晰的键盘焦点指示,焦点环不得被分段容器的圆角或 `overflow` 裁切。
- 右侧 Supervisor 对话中,用户消息使用右对齐、最大宽度受限的主题暖色气泡,assistant 消息保持左对齐;消息换行不得产生水平溢出,执行过程卡继续占满消息区可用宽度。消息列表必须约束在右侧对话列内并独立滚动,不得覆盖中央资源或运行视图;提交按钮必须保留随状态变化的可访问名称。气泡正文在 light / dark 平台主题下均须满足 WCAG AA 普通文本 `4.5:1` 对比度。
- 客户端正式产品仍只按最小 `1280×720` 横屏合同交付,并保留 `1280×800` 默认窗口与既有基线验收;更窄浏览器样式只负责不崩溃和开发兼容,不改成移动端创作工作台。
- 工作台顶部播放按钮在桌面视窗中水平居中;资源管理视窗触发播放时直接切换到运行视窗并启动本地预览,不再弹出 `game.run_local` 二次确认。
- 工作台顶部播放按钮紧贴「资源管理 / 运行」模式切换之后、整组左对齐;不再在桌面视窗中水平居中悬浮;两个页签下都常驻(运行视图空态与预览失败态要靠它重跑)。资源管理视窗触发播放时直接切换到运行视窗并启动本地预览,不再弹出 `game.run_local` 二次确认。
- 创建模式素材画布的“素材名称”是用户可编辑的正式输出名称;“资源用途”是 manifest subtype,不向普通用户开放自由文本。新增资源默认“普通游戏美术”,可从普通游戏美术、统一视觉规范、游戏界面原型、核心美术图集四项中选择。图片精修继承源名称和用途,不显示创建模式保存设置;候选图片只从选中图片的“设为最终图”提交。精修顶栏只保留返回、导入、定位当前最终图、撤销和重做,删除进入图片上下文工具栏,通用 AI 生成只保留给创建模式。图片输出统一使用 PNG;创建模式工具动作与保存设置分层展示,“保存到项目”在 `1280×800` 和窄容器中都必须完整可见。
- 首页创作输入区与“最近项目”之间不展示共享项目状态文本,“最近项目”标题下也不追加解释性副标题;默认、成功、进行中或失败状态均不得在该位置形成文字行,项目管理页继续保留自己的状态反馈。
@@ -142,7 +148,7 @@
### 3.10 功能画布底部工具栏(入口矩阵)
实现状态(2026-09-13):功能画布 = 资源栏目页(`resourceBookState.view === 'child'`)。栏目页左下角的底部工具栏按 manifest 资产的**功能分类** `category` 分流,矩阵由用户 2026-09-13 拍板,工具项顺序即下表顺序(最右一项为「上传」)。
实现状态(2026-09-132026-09-14 图片类生成后台化:提交即返回 + 项目内任务账本 + 本地排队 + 非模态「生成任务」面板):功能画布 = 资源栏目页(`resourceBookState.view === 'child'`)。栏目页左下角的底部工具栏按 manifest 资产的**功能分类** `category` 分流,矩阵由用户 2026-09-13 拍板,工具项顺序即下表顺序(最右一项为「上传」)。
| 栏目 | 工具栏里的生成入口 |
| --- | --- |
@@ -154,11 +160,12 @@
- 位置与层级:画布左下角(`left: 14px; bottom: 14px; z-index: 40`)。右下角是既有的缩放 / 撤销 Dock(`right: 14px; bottom: 14px`),左下角是画布上唯一两者都不占的稳定空位。工具栏是管理区 `.game-resource-book-manager` 的**直接子节点**、与画本场景并列,不进带 `scale()` 的场景层;二级菜单与「入口不可用原因」都贴着工具栏上沿弹出,不做内嵌内容。
- 外壳用共享 chrome`packages/image-canvas-react``CanvasToolbar / CanvasToolbarGroup / CanvasChromeButton`),样式落在 AGC 的 `resourceCanvasChrome.css`;共享包只承接通用表现,不含业务规则。
- 接线:图片类入口走本地 IPC `generate_local_project_asset``kind``image / character / spec / icon-spec / ui-prototype / art-spritesheet`);音频入口复用既有的无源生成链路 `derive_local_project_resource``generationMode: 'create'``editKind` = `sound-effect` / `background-music`);「上传」复用 `upload_local_asset`。生成 / 上传成功后一律用「配对读 `(revision, manifest)`」交给 `onManifestChange`,走既有 manifest 刷新与资源投影链路,不重算依赖图、不另写布局。
- 面板形态:独立浮层(`ThemedModal`),**不在当前面板下面追加内容**;面板内不写功能说明或规则解释文案
- 接线:图片类入口走本地 IPC `start_local_project_asset_generation``kind``image / character / spec / icon-spec / ui-prototype / art-spritesheet`;**提交即返回任务记录**,生成由 Rust 后台任务跑完写回项目,进度用 `list_local_project_asset_generations` 读回项目内账本 `.agent/runtime/asset-generation-tasks/tasks.json`);音频入口复用既有的无源生成链路 `derive_local_project_resource``generationMode: 'create'``editKind` = `sound-effect` / `background-music`);「上传」复用 `upload_local_asset`。生成 / 上传成功后一律用「配对读 `(revision, manifest)`」交给 `onManifestChange`,走既有 manifest 刷新与资源投影链路,不重算依赖图、不另写布局。
- 本地排队与进度可见:AGC 本地 durable 输出槽已按**精确动作指纹**分槽(不同 prompt / 素材名各自独立成槽,具备并行能力),但本批前端仍按「同一时刻只派发一条」排队——真并行派发需要并发收口设计(配对读 + manifest CAS + 聚焦意图互不覆盖),留待下一批;所以第一条未终态时第二条提交停在**前端本地队列**里(不调用提交 IPC,显示本地排队的「排队中。」),前一条终态后自动补发;任务状态与阶段文案(后端 `phaseDetail`)由任务账本提供,前端不拼阶段、不做百分比。进度面是**画布上常驻的可折叠任务侧栏**(形态对齐网页端美术画布的任务侧栏):展开是两个分栏「排队/生成中」与「已完成」(各带条数,「已完成」封顶 20 条 + 列表滚动 + 高度有界),关闭入口只保留头部那一枚 ×(底部重复的关闭按钮与其分割线已删除)、折叠即整块让出画布且不留贴边把手,开合只走工具条上常驻的「生成任务 · N」按钮(两个页签下都在);每项显示状态徽标 / 阶段文案 / 已耗时 / 素材名,可「定位到素材」。侧栏非模态(不铺全屏遮罩、不做焦点陷阱、不参与模态遮挡判据),位置在画布左侧标题栏之下、**覆盖式**(不 reflow 挤窄画布视口),提交受理后自动展开。定位动作**每次点击都终局化**:能定位就定位并选中;素材在别的栏目先切栏目;不在投影里给「素材已不在项目里 / 已登记但尚未同步」的结论;3 秒内有界兜底,不允许提示条永久停在「正在定位生成的素材…」
- 面板形态:独立浮层(`ThemedModal`),**不在当前面板下面追加内容**;面板内不写功能说明或规则解释文案。**点「生成」即同步关闭面板**(不等 IPC、不等排队、不等生成),面板里**不出现**「排队中。」「正在生成。」「提交中…」这类阶段文案——阶段文案的唯一去处是任务侧栏与工具栏提示条。**只有「点击瞬间就失败」**(校验不过、权限拒绝、start IPC 立即报错)才自动重开面板并带回草稿与原因;**受理之后才失败**只在侧栏把该任务收口为失败 + 原因,不重开面板。关闭 ≠ 取消请求(请求挂在任务与账本上,不挂在面板生命周期上)。
- 参数口径:比例 / 尺寸选项来自网页端美术画布的纯模型(`src/components/image-editor/ImageCanvasGenerationModel.ts`),并按本地 IPC 白名单收窄(本地通道明确拒绝 `4:3`);默认档 `1:1 · 1K`,生成 UI 设计图沿用网页端 UI 设计面板的默认 `16:9 · 1K`。本地 IPC 没有 `model` 入参,因此面板**不渲染模型选择器**(渲染一个改不了请求的控件就是假控件)。
- 规范类和固定档:生成规范下的图标规范 / 角色规范 / 自定义规范都是固定档——只读展示当前规格,不给点了不生效的比例控件。本地 IPC 只有一条规范通道(`spec` 在 Rust 侧收口到 `icon-spec`),**没有 `specType` 入参**,因此角色规范与自定义规范共用该通道,靠素材名与提示词区分。
- 前置规范图:`generate_local_project_asset``ui-prototype``art-spritesheet` 要求项目里已有登记并绑定当前账号的 `assets/art-spec.png``kind: icon-spec`、图片媒体类型、来源为画布)。缺前置时对应入口**保持可点击**并给出可执行的原因与指路(仓库口径:视觉不可用但可点击说明原因,不用原生 `disabled`),原因文案包含 `assets/art-spec.png` 与「生成规范 → 图标规范」。
- 前置规范图:`start_local_project_asset_generation``ui-prototype``art-spritesheet` 要求项目里已有登记并绑定当前账号的 `assets/art-spec.png``kind: icon-spec`、图片媒体类型、来源为画布)。**后台化没有改这条前置判据**:Rust 侧仍是同一口径,前端 `projectHasIconSpecReference` 仍与它逐字对齐,缺前置时对应入口**保持可点击**并给出可执行的原因与指路(仓库口径:视觉不可用但可点击说明原因,不用原生 `disabled`),原因文案包含 `assets/art-spec.png` 与「生成规范 → 图标规范」。
- 权威规范图由工具栏自身可满足:「图标规范」入口在项目尚无权威规范图时按 `assets/art-spec.png` 落盘;已有权威规范图时**不再传 `outputPath`**Rust 侧 `replace_existing` 固定为 `false`,指向已存在文件会被硬拒绝「outputPath 已存在,禁止静默覆盖」),改为生成一张新的普通图标规范资产。这条前端策略不扩接口。
- 音频入口收敛:音频画布的「生成背景音乐 / 生成音效」由工具栏承载,既有「生成素材」浮层入口**只保留生成视频**(视频能力不删,只是不出现在工具栏里)。
- 本轮明确不做:生成视频(保留在既有浮层入口)、宣发素材、生成游戏场景、画布级「选择工具 / 抓手工具」(AGC 现无画布级工具模式)。这五类都**不得**出现在工具栏里。
@@ -652,11 +659,12 @@ type ProjectAgentMudPointAttribution = {
1. 工具栏只在资源栏目页且分类命中矩阵四个栏目时渲染;资源总览、「所有资源」展开态、文档 / 待归类 / 项目版本都不渲染。稳定 DOM 判据 `[data-resource-bottom-toolbar="<category>"]`;总览与「所有资源」展开态下该节点必须不存在。
2. 工具栏是管理区 `.game-resource-book-manager` 的直接子节点、与画本场景并列(`closest('.game-resource-book-scene')``null`),且与右下角 Dock 互不抢位(左下角锚定:`left` 有值、`right` 无声明;Dock 相反)。二级菜单只在展开时出现,展开时可被外部点击 / Esc 收起。
3. 每个图片类入口一次提交**一条** `generate_local_project_asset`,载荷字段逐字为 `{ projectPath, kind, prompt, aspectRatio, imageSize, assetName, outputPath }``kind` 映射:生成图片 `image`、生成角色形象 `character`、图标规范 `icon-spec`、角色规范 / 自定义规范 `spec`、生成图标素材 `art-spritesheet`、生成 UI 设计图 `ui-prototype`
3. 每个图片类入口一次提交**一条** `start_local_project_asset_generation`,载荷字段逐字为 `{ projectPath, projectId, taskId, kind, prompt, aspectRatio, imageSize, assetName, outputPath }`——**原同步命令的生成载荷字段名与取值口径逐字不变**(`kind / prompt / aspectRatio / imageSize / assetName / outputPath` 全部原样搬过来),只多了任务身份 `projectId` 与前端每次提交新铸的本地 `taskId``kind` 映射:生成图片 `image`、生成角色形象 `character`、图标规范 `icon-spec`、角色规范 / 自定义规范 `spec`、生成图标素材 `art-spritesheet`、生成 UI 设计图 `ui-prototype`命令**提交即返回**一条任务记录,生成在 Rust 后台任务里跑,状态与阶段由 `list_local_project_asset_generations` 从项目内账本读回。
4. 比例 / 尺寸选项**不含**本地通道拒绝的 `4:3`;比例与尺寸的切换都进入载荷;固定档规范入口只读展示当前规格(如 `1:1·1K`),不渲染可点比例控件。
5. 缺权威规范图时「生成图标素材 / 生成 UI 设计图」保持可点击、给出含 `assets/art-spec.png` 的原因说明,且**不发出任何生成请求**;「图标规范」此时把 `outputPath` 指向 `assets/art-spec.png`;项目已有权威规范图时 `outputPath``null`(两种状态各一条用例)。
6. 生成 / 上传成功后走既有刷新路径:`get_local_game_project_revision` + `get_local_game_manifest` 的配对读发生在写入命令之后,结果交给 `onManifestChange`;音频入口提交的是 `derive_local_project_resource``editKind` = `sound-effect` / `background-music``generationMode: 'create'``sourceMediaType: 'audio/mpeg'`)。
6. 生成 / 上传成功后走既有刷新路径:`get_local_game_project_revision` + `get_local_game_manifest` 的配对读发生在**该任务收口之后**(提交命令不再等待生成,配对读由任务终态驱动,顺序仍在写入之后,结果交给 `onManifestChange`;音频入口提交的是 `derive_local_project_resource``editKind` = `sound-effect` / `background-music``generationMode: 'create'``sourceMediaType: 'audio/mpeg'`)。
7. 既有「生成素材」浮层入口只呈现视频:面板不再渲染音效 / 背景音乐类型选择器(单类型入口没有类型选择器),音频入口只出现在音频栏目工具栏。
8. 生成期间可退出与本地排队(2026-09-14):提交后点 × / 遮罩 / Esc /「后台运行并关闭」任一都能关面板,画布立即恢复可交互,关闭不取消请求;面板关闭后任务仍在「生成任务」面板(非模态浮层,无 `aria-modal`)里显示后端 `phaseDetail`;第一条未终态时提交第二条 → 第二条显示「排队中。」且生成提交 IPC 次数仍为 1,第一条终态后自动补发(次数为 2);两条各自完成后各走一次「配对读 + 落卡」,manifest revision 单调、不被后到者覆盖。
## 8. 非目标
@@ -0,0 +1,35 @@
# AGC 统一错误诊断与验收反馈实施计划
Version: 1.0
Status: active
Date: 2026-09-15
Parent Milestone: `【里程碑】AGC统一错误诊断与验收反馈-2026-09-15.md`
## 修改边界
1. 新增 `agent/runtime_error.rs`,承载统一事件字段、code/stage 白名单、脱敏后的 public projection、项目错误 JSONL/sidecar 落库和 detail 读取边界。
2. `direct_runtime.rs` 使用统一事件替代仅写 `failure.json` 的路径;失败 assistant 投影带稳定 ID,下一轮 prompt 注入最近失败事件摘要。
3. `codex_app_server.rs` 将 failed turn、idle/hard timeout、transport close、invalid terminal 和 stderr tail 转成稳定事件字段;不公开原始 detail。
4. `direct_tool_bridge.rs``direct_tools_mcp.rs` 让 attempt 由客户端回合状态约束,越界请求返回终态工具错误;不扩展重试预算。
5. `direct_runtime.rs` 的素材扫描递归覆盖可执行源码模块,基于 manifest 身份和浏览器 URL 映射判定;补充模块引用回归测试。
6. 前端读取后端 `publicText/detailRef`,在现有 Runtime 错误面板中加入详情入口;不在 React 侧重新分类错误。
## 实现顺序
先写统一事件模型和 Rust 单测,再接 direct failure/app-server/tool bridge,随后接 prompt/history 与前端详情,最后修素材验收和 attempt 生命周期。每一步保留原有脱敏和失败关闭行为。
## 验证命令
- `cargo fmt --check`
- `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml runtime_error direct_runtime codex_app_server direct_tool_bridge`
- `npm run --prefix apps/ai-game-creator-shell typecheck`
- `npm run check:encoding`
- `git diff --check`
- 必要时运行 AGC deterministic playable E2E;真实 Provider smoke 与浏览器双视口 smoke 单独报告。
## 风险与回滚
- 统一事件 schema 只新增项目内文件和对话投影,不修改已有 manifest、公开 API 或 SpacetimeDB schema。
- 若前端详情读取失败,仍展示安全 `publicText`,不阻塞错误终态。
- 若素材身份无法映射,继续失败关闭并记录明确 code,不回退为路径字符串通过。
- 回滚可删除新事件写入和详情入口,保留旧 `failure.json` 读取兼容。
@@ -0,0 +1,20 @@
Version: 1
Status: active
Date: 2026-09-15
Parent Spec: 【里程碑】AGC首页策划补全入口-2026-09-15.md
## 修改顺序
1.`view/home/index.tsx` 增加本地复选框状态与入口切换清理。
2. 将游戏勾选状态映射到既有 `ProjectStartMode`
3. 增加启动模式纯函数测试,覆盖模式分流;首页显示边界和切换清理作为后续组件测试补充项。
## 验证
- AGC 首页相关定向测试。
- AGC 前端 typecheck。
- `npm run check:encoding``git diff --check`
## 当前验证边界
本次已交付测试覆盖 `planning` / `direct-build` 模式分流;“策划补全”复选框的显示边界及切换创作类型后的状态清理尚未有组件级自动化测试,需后续补充 `HomeView` 测试时完成。
@@ -0,0 +1,37 @@
# 【实施计划】DirectProject Thread Manager 事件订阅
| 字段 | 值 |
| --- | --- |
| Milestone | `docs/project-memory/plans/【里程碑】DirectProject Thread Manager事件订阅-2026-09-15.md` |
| Status | ready |
| Owner | Codex |
## 修改边界
- 允许修改:DirectProject Rust Thread Manager 深模块、app-server 事件适配、Tauri command/event 桥接、DirectProject 前端订阅/reducer/历史加载、对应测试和主规范。
- 明确不修改:SpacetimeDB、HTTP API、非 DirectProject Runtime、Codex app-server durable thread、用户可见 JSONL 细节。
- 保持已有 `.env` 未提交修改,不触碰个人配置。
## 实现顺序
1. 先新增独立 Rust queue/subscriber 深模块,只承载事件追加、逻辑队头回收、subscriber cursor 锁和纯单测。
2. 将 app-server 公开事件安全标准化后接入 Thread Manager;在 item 完成持久化成功后追加完成事件,并追加 turn 生命周期事件。
3. 增加 Tauri `subscribe/consume/readHistory` 命令与 notify 事件,固定错误和 bootstrap 原子边界。
4. 前端改为 subscriptionId 驱动的 raw event reducer;重进/过期时先 bootstrap,完成后原子替换;历史按 itemId 懒加载。
5. 移除 DirectProject legacy conversation 读取分支,补齐契约、并发、恢复和失败关闭测试;让初始历史切片的 `hasMore` 独立驱动“显示更早”按钮和滚动入口。
6. 每个独立切片分别运行定向验证并形成中文小提交;里程碑验收后再清理临时计划。
## 验证命令
1. `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml` 的 DirectProject/Thread Manager 定向测试。
2. 相关前端 Vitest 与类型检查。
3. `npm run check:encoding`
4. `npm run check:doc-index`
5. `git diff --check`
## 风险与回滚点
- 现有 app-server 事件模型与公开 raw event envelope 不完全一致:先在适配层收口,不让协议细节泄漏到前端。
- 单 Vec 队列不能中间删除;unfinished item 长时间不结束可能暂时 pin 住队头,必须保留可观测上限和测试。
- Tauri command 无传输层断开回调,subscriber 只通过 queue eviction 失效;测试不能依赖 unsubscribe 或连接断开清理。
- legacy 删除属于 breaking history 行为;失败关闭测试必须确认不会 fallback 或迁移。
@@ -0,0 +1,36 @@
# 【实施计划】DirectProject 用户 Response item 输入
| 字段 | 值 |
| --- | --- |
| Milestone | `docs/project-memory/plans/【里程碑】DirectProject用户ResponseItem输入-2026-09-15.md` |
| Status | in-progress |
| Owner | Codex |
## 修改边界
- 允许修改:AGC 壳 Rust agent 输入合同、DirectProject 历史适配、前端聊天引用模型、ts-rs 生成配置、当前聊天素材文档。
- 明确不修改:assistant 返回协议、工具 activity、附件/图片协议、SpacetimeDB、HTTP API。
## 实现顺序
1. 提取深模块:Rust canonical user item 的定义、校验和 Codex wire 转换。
2. 生成并接入 ts-rs 类型,排除生成文件 lint/style。
3. 将前端 Lexical 草稿从 `text + references[]` 改为 inline content parts。
4. 修改 Tauri command 与 DirectProject turn:校验通过后持久化 canonical item,再发送转换后的 Codex 输入。
5. 删除本链路对 legacy conversation 行的读取 fallback,保留标准 `response_item`
6. 补齐定向测试与文档验证;每个独立切片形成小提交。
## 验证命令
1. `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml direct_codex`
2. 相关前端 Vitest 与类型检查。
3. `npm run check:encoding`
4. `npm run check:doc-index`
5. `git diff --check`
## 风险与回滚点
- ts-rs 生成路径或 workspace lint 配置不一致:先固定生成入口,再接入业务。
- 历史中 canonical item 含 AGC part 时,thread replay 必须经过同一 wire converter;转换失败不得启动 turn。
- 共享工作树存在用户 `.env` 修改,禁止覆盖或提交。
- 每个切片保持独立提交,出现协议问题时按提交粒度回滚。
@@ -0,0 +1,51 @@
# 【实施计划】Direct回合跨页面生命周期与运行中项目可见性-2026-09-15
Version: 1
Status: in-progress
Date: 2026-09-15
Milestone: `【里程碑】Direct回合跨页面生命周期与运行中项目可见性-2026-09-15.md`
## 固定契约
只读快照命令(Tauri 本地命令,`src-tauri/src/agent/direct_runtime/mod.rs`):
- `list_game_creator_direct_active_turns() -> Vec<GameCreatorDirectActiveTurn>`
- 字段(camelCase):`projectPath``projectName``turnId``status``activity`(可空)、`startedAt``updatedAt``sequence`
- `status` 取值集合与既有 Direct 回合事件一致:`accepted` / `running` / `streaming` / `finalizing` / `completed` / `failed`
身份锁与快照共用同一份进程内注册表;注册表条目在回合进入时写入 `startedAt``projectName`,在每次回合事件发射时更新 `status` / `activity` / `sequence` / `updatedAt`,在回合结束(guard drop)时移除。
## 代码边界
- `apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs`:注册表结构扩展、快照读写、新命令、Rust 定向测试
- `apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/entrypoints.rs`:事件发射时投影到注册表
- `apps/ai-game-creator-shell/src-tauri/src/main.rs`:命令注册
- `apps/ai-game-creator-shell/src/App.tsx`:项目打开时重连、忙碌态与进度恢复、面板挂载
- `apps/ai-game-creator-shell/src/features/agent-runtime/directActiveTurns.ts`:快照轮询与单飞刷新(面板与重连共用)
- 面板组件(新文件,落在既有 feature 目录下)+ 对应测试
- `apps/ai-game-creator-shell/src/features/agent-runtime/model.ts` + 测试:报错归类修正
## 修改顺序
1. Rust:扩展活动回合注册表并暴露只读快照命令,配定向用例(进入 / 进度 / 终态移除 / 多项目并存)。
2. 前端:接入快照读取,实现“重新进入项目 → 恢复忙碌态与进度 → 以快照 sequence 续接 → 阻止并发提交”。
3. 前端:在窗口标题栏挂载“正在运行的项目”下拉入口;标题栏只显示最后开始的项目,展开后按开始时间列出全部项目,复用既有组件与设计 token。
4. 报错归类:按审计结论修正会误导的映射,逐条加回归用例;真实权限拒绝保持原提示。
5. 文档:主规范与共享记忆同步;里程碑验收后删除临时计划文件。
## 验证命令
- `cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml direct_active_turns -- --test-threads=1`(名称按实际用例调整)
- `npx vitest run apps/ai-game-creator-shell/tests/agentRuntimeModel.test.ts`
- `npx vitest run apps/ai-game-creator-shell/tests/appSurface.test.ts`
- 面板组件测试文件单独一条 vitest
- `npm --prefix apps/ai-game-creator-shell run typecheck`
- `npm run check:encoding``git diff --check`
## 风险与回滚点
- 快照命令暴露项目绝对路径给前端:与现有 `projectPath` 口径一致,不得额外泄露配置或 token;命令必须是只读、无副作用。
- 续接基线 `sequence` 若取错,会让重新进入后的进度事件被丢弃或重复消费;取错时回滚“重连”部分,保留只读面板。
- 忙碌态恢复不得与既有 `chatAgentBusy` 的失败清理互相覆盖;出现卡死忙碌态时优先回滚重连,不影响身份锁与后台回合本体。
- 面板若在窄窗口挤压主内容,先按既有响应式约定隐藏面板,不改主布局。
- 报错归类修正若与既有断言冲突,先确认断言锁的是“正确行为”还是历史错误文案,再决定改断言还是改实现。
@@ -0,0 +1,12 @@
# 对话回合唯一投影实施计划
对应:[对话回合唯一投影](./【里程碑】对话回合唯一投影-2026-09-16.md)。
本次增量顺序:先移除 Provider 回放对活动 client 回合的写入,复用活动快照同步并处理过期异步结果;再为历史信封及切片增加可选时间映射;最后在现有回合投影中分离最终回复和可折叠过程,复用 details 与工具组件。新增恢复、时间、折叠边界用例,只运行静态检查,不运行测试;按最新授权本地提交,不推送。
1. 前端抽取纯回合呈现投影,完整历史关联后分页;删除旧的消息锚定/实时/未归属独立渲染分支。
2. Rust 按 item 完成边界冲刷,段切换返回全部快照;收尾等待写任务,修正 upsert 与跨回合裁剪。
3. 核对 MCP 输入输出与同状态更新,保留现有脱敏。
4. 自审正常、失败、历史无流、分页和重复快照路径;只执行定向 tsc、cargo check、check:encoding、check:doc-index 与 git diff --check,不运行测试;按最新授权提交到本地,不推送。
风险:旧流可能只有前缀或部分回合;只依据原始 item 身份补齐,不能靠长度比例推断。实机须重启 Rust 客户端后验收。回滚只撤销本次触及的实现片段,保留工作树原有样式和输入输出修改,不改用户项目数据。
@@ -0,0 +1,17 @@
# 文档素材 Markdown 预览实施计划
- Date: 2026-09-16
- Status: implemented-awaiting-validation
- Milestone: `【里程碑】文档素材Markdown预览-2026-09-16.md`
## 边界与顺序
1. 扩展现有 Markdown 渲染器的代码高亮,未知语言、大正文安全降级;文档模式保留原始空行。
2. 用资源现有预览类型和扩展名生成 Markdown;代码只放行 `detail` 读取。
3. 工具栏挂载预览入口,复用 ThemedModal 和预览缓存展示加载/空/失败状态,身份变化关闭旧预览。
4. 补充格式转换、渲染和按需读取回归用例,不执行测试。
5. 执行 AGC TypeScript、编码、文档索引、CSS 语法与 diff 检查。
## 风险与回滚
重点检查高亮 class 不被自定义 code 渲染丢弃、Markdown 围栏不吞正文、弹窗 Esc 不误清画布选中、文档切换不串内容。回滚仅移除本次入口/渲染增量,保留此前工具栏修改,不改资源数据。
@@ -0,0 +1,39 @@
# AGC 统一错误诊断与验收反馈
Version: 1.0
Status: active
Date: 2026-09-15
Parent Spec: `docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md` 的“2026-09-15 AGC 统一错误事件、诊断落库与验收反馈”
## 目标
让 DirectProject 和共享 Agent Runtime 对失败使用同一份安全、可追踪、可恢复的错误事件合同;用户追问失败原因时能够读取上一轮证据;构建与浏览器验收只依据真实源码、manifest 身份和运行时证据判断。
## 范围
- 统一错误事件模型与项目内诊断落库。
- DirectProject 失败 assistant 投影、下一轮诊断上下文和前端详情入口。
- app-server 终态/超时、内置 MCP 工具错误和试玩 attempt 上限的分类。
- 游戏源码模块素材扫描、manifest 身份映射与浏览器观察映射。
- 定向 Rust/前端回归和现有 AGC 运行时门禁。
## 不做
- 不改变 Provider、External Editor 或 app-server 的 wire 协议。
- 不放宽项目写锁、凭据隔离、工具白名单或完成门安全边界。
- 不迁移历史项目文件;旧诊断只读兼容,新增事件使用新 schema。
- 不把原始 stderr、请求正文或绝对路径展示给用户。
## 验收标准
1. 任一 DirectProject 失败均生成统一事件、稳定 `eventId` 和有界诊断引用;落库失败不覆盖原始错误。
2. 失败安全投影写入对话历史,下一轮能读取 `publicText / code / stage / detailRef`,不会因追问而自动试玩。
3. 结构化 failed turn、idle/hard timeout、transport close、MCP 参数错误和 `other` 各有稳定 code 与 recoveryHint。
4. `attempt` 由客户端按回合分配并有上限;越界调用不会让回合继续等待。
5. `game/src` 下模块引用已登记素材、Vite dist 稳定映射和浏览器实际观察均能通过;未登记素材仍失败。
6. 脱敏测试证明 Token、Cookie、URL/query、私钥、宿主绝对路径和 stderr 私密内容不会进入用户文本。
## 依赖
- 现有 `direct_project_history``runtime_state``codex_app_server``direct_tool_bridge` 与浏览器 validation 证据。
- 现有 DirectProject 诊断 sidecar 和 manifest 资源身份。
@@ -0,0 +1,20 @@
Version: 1
Status: active
Date: 2026-09-15
Parent Spec: AGC 首页与 Agent Runtime 入口
## 范围
在首页“做游戏”输入框下增加“策划补全”复选框;勾选后复用现有 `planning` 启动模式进入策划 Agent Runtime。
## 验收标准
- 仅“做游戏”显示复选框。
- 勾选时提交 `planning`,未勾选时提交 `direct-build`
- “做方案”原有 `planning` 行为保持不变。
- 切换到其它创作类型时清除游戏专属勾选状态。
## 不做项
- 不新增 runtime 类型、后端接口或持久化字段。
- 不改变现有策划 runtime 内部流程。
@@ -0,0 +1,53 @@
# 【里程碑】DirectProject Thread Manager 事件订阅
| 字段 | 值 |
| --- | --- |
| Version | 1.0 |
| Status | ready |
| Date | 2026-09-15 |
| Parent Spec | `docs/technical/【技术方案】DirectProject Codex原始历史与异常恢复-2026-09-04.md` |
## 目标
让 DirectProject 对话在页面离开、重进和短暂断线后仍能由前端重建运行态;运行态事件由 Tauri 进程级 Thread Manager 管理,已完成 item 继续以 `project.jsonl` 为持久化事实源。
## 范围
- 每 thread 一个全局 seq 和 append-only replay queue。
- 每 subscriber 独立的 Rust 内部 cursor、并发安全消费和 notify 唤醒。
- `subscribe` bootstrap、`consume``SUBSCRIPTION_EXPIRED` 和历史 item 锚点。
- app-server 公开事件的安全标准化、item 持久化先于完成事件转发。
- 前端 raw event reducer、历史懒加载和过期重订阅。
- 删除本链路 legacy conversation 格式支持,不提供 fallback 或 migration。
## 不在范围内
- SpacetimeDB、HTTP API、Codex thread durable recovery。
- 新的 item durable/status/pendingInteraction 字段或持久化确认事件。
- 前端访问 JSONL 路径、格式或持久化细节。
- 多 active turn;同一 thread 仍只有一个 active turn。
## 依赖与前置条件
- DirectProject 现有 app-server 事件解析和 `project.jsonl` 读写。
- 当前 Tauri command/event 注册入口。
- 现有前端 DirectProject 聊天 reducer 与历史加载入口。
## 验收标准
- [ ] 页面离开后 app-server 回合继续,重进页面能通过 subscribe 重建 unfinished item。
- [ ] 同一 thread 的多个 subscriber 各自消费,不互相覆盖或重复推进 cursor。
- [ ] `consume` 返回 cursor 之后的全局 raw events,通知不携带 payload。
- [ ] queue eviction 只清理队头;落后 subscriber 得到 `SUBSCRIPTION_EXPIRED` 并可重新 subscribe。
- [ ] item 完成先持久化,成功后才进入完成事件队列;失败不发送正常完成事件。
- [ ] `turn.completed` 由 app-server 终态进入 raw queue,前端据此结束运行态。
- [ ] subscribe 返回 item 历史锚点而不是完整 history;前端可按 itemId 懒加载。
- [ ] 未完成 item 的每个 delta 可从 `item.started` 开始重放;不截断 active item。
- [ ] legacy conversation 行直接失败关闭,无 fallback、无迁移。
- [x] DirectProject 首页历史切片存在 `hasMore` 时,即使当前可见窗口没有隐藏消息,也提供按钮和滚动两种“显示更早”入口。
## 证据要求
- 自动化:queue/cursor/eviction 并发单测、事件标准化和持久化顺序测试、Tauri command 测试、前端 reducer 与重订阅测试。
- 运行时:关闭/切页后重进 DirectProject;并发 item;短暂断线 consumecursor 过期重订阅。
- 边界:持久化失败、未知 subscription、queue 超限、多个 subscriber、turn 无 item 间隙、legacy 行拒绝。
@@ -0,0 +1,51 @@
# 【里程碑】DirectProject 用户 Response item 输入
| 字段 | 值 |
| --- | --- |
| Version | 1.0 |
| Status | proposed |
| Date | 2026-09-15 |
| Parent Spec | `docs/【功能说明】AGC聊天素材引用-2026-09-08.md` |
## 目标
将 DirectProject 的用户消息升级为受限 Response API `message` item:文本与 AGC 引用按编辑顺序内联,Rust 校验后持久化 canonical item,并转换为 Codex 可接受的输入。
## 范围
- Rust 定义 user item 与 content part,并通过 `ts-rs` 生成 TypeScript 绑定。
- 前端 Lexical 草稿生成 inline `content[]`
- `agc_resource_reference` 只使用稳定 `resourceId`
- `agc_runtime_region_reference` 保留运行区域语义摘要。
- Rust 在持久化前完成白名单、manifest 与路径校验。
- 现有标准 `response_item` 原样兼容;legacy conversation 行不提供 fallback。
- 保持 assistant 返回、工具 activity、附件/图片协议不变。
## 不在范围内
- assistant item 前端投影或 Tauri 返回值改造。
- 工具 item、reasoning、file change、MCP item 的 UI 模型化。
- 附件/图片 content part。
- SpacetimeDB schema 或 HTTP API 变更。
## 依赖与前置条件
- DirectProject 现有 app-server thread/inject_items/turn/start 链路。
- 项目 manifest 作为资源身份与路径权威。
- 现有 `project.jsonl``response_item` envelope。
## 验收标准
- [x] 前端生成的 canonical user item 保留 Lexical 中文本与引用的相对顺序。
- [x] `agc_resource_reference` 仅包含 `resourceId`,显示信息由 manifest 派生。
- [x] runtime-region 字段经过 Rust 有界清洗并验证关联资源。
- [x] 未知 part、失效资源或非法路径在持久化前失败关闭。
- [x] canonical item 以 `response_item` 写入历史,标准旧 item 原样可读。
- [x] Codex wire input 不含 AGC 私有 part,且顺序与 canonical content 一致。
- [ ] assistant、附件和工具链路行为无变化。
## 证据要求
- 自动化:Rust item 校验/转换/历史测试;前端草稿顺序与类型测试;ts-rs 生成检查。
- 运行时:DirectProject 本地 app-server smoke(如环境可用)。
- 边界:未知 part、资源删除、非法路径、重复提交 clientTurnId、legacy 行拒绝。
@@ -0,0 +1,33 @@
# 【里程碑】Direct回合跨页面生命周期与运行中项目可见性-2026-09-15
Version: 1
Status: in-progress
Date: 2026-09-15
Parent Spec: `docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`「2026-09-15 Direct 回合跨页面生命周期与运行中项目可见性」
## 目标
离开项目界面不再等于“回合消失”:后台继续跑的 Direct 回合必须能被前端重新发现并续接进度,同一项目在回合结束前不允许再发起第二条付费回合;壳层窗口标题栏提供“正在运行的项目”下拉入口,常态显示最后开始的项目,展开后列出当前确有在跑回合的全部项目并可点击进入。
## 边界
- 只读投影:新增命令只读当前 GUI 进程内的活动回合注册表,不写项目文件、不新增持久化账本。
- 不新增取消入口;不改变身份锁排他性、项目写锁语义、计费与幂等身份。
- 不新增跨端契约(Tauri 本地命令,不进 `packages/shared` / `shared-contracts` / OpenAPI)。
- 面板与重连共用同一份快照,不各自维护第二份“谁在跑”的真相。
- 报错归类修正只处理“说明与真相无关”的情况,不放宽身份锁、不吞真实失败。
## 验收标准
- 重新进入有在跑回合的项目后:界面进入“正在处理”、显示最近一次进度、以快照 `sequence` 续接后续事件;回合结束前提交第二条需求不会真正发起第二条付费回合。
- 回合结束(completed / failed)后:忙碌态解除、可以再次发送;不重复追加助手消息。
- 无在跑回合的项目:行为与今天一致(可正常发送,不出现额外提示或阻塞)。
- 窗口标题栏入口:常态只显示最后开始的项目,点击后按 `startedAt` 升序列出所有在跑项目,显示项目名(缺失时回退目录名)与状态/时长,点击进入对应项目;没有在跑回合时不渲染入口。
- 快照读取失败不得阻断发送、不得显示成业务失败。
- 已修的错误映射不回归:`direct-codex-turn-already-running:` 与历史同义中文正文都归一到“仍在处理这个项目的上一条需求”;真正的 `项目权限策略拒绝执行:<command>` 仍显示审批提示。
## 未决事项
- “离开页面即取消”仍是未采纳的另一种语义;本轮只实现后台继续。
- 应用重启后的“未完成回合”恢复不在本里程碑范围(回合注册表是进程内状态);若未来要求跨重启恢复,需要另立里程碑并定义持久化身份与对账合同。
- 面板是否需要展示非 Direct(专业 Agent / 策划 Agent)运行中的项目,本轮不做;先把 Direct 回合这条事实链路做正确。
@@ -0,0 +1,214 @@
# 【里程碑】Provider 推理与正文分离及策划 Agent 展示
| 字段 | 值 |
| --- | --- |
| Version | 1.0 |
| Status | in-progress |
| Date | 2026-09-14 |
| Parent Spec | `docs/technical/【技术方案】策划Agent生产迁移与工作区浏览-2026-09-10.md` |
| Related Issue | `GenarrativeAI/Genarrative#331` |
## 一句话交付结果
让策划 Agent 能在流式回合中单独收到 Provider reasoning,并在 UI 中以默认折叠的思考过程展示;用户可见正文、工具调用和 GameAgent 现有行为保持不变。
## 当前实现进度(2026-09-14
- 已完成共享 reasoning 字段、Provider 解析、策划事件映射以及正文流式收尾的前两轮提交。
- 当前第三轮聚焦策划 Agent 前端 reasoning 生命周期:按 `projectPath + clientTurnId` 绑定事件,回合结束后保留本轮 reasoning,下一轮或项目切换时清理。
- 现有 `<details>` 展示保持默认收起,并提供明确的展开/收起入口;不新增持久化字段,也不改动 GameAgent、Direct/Codex、supervisor 或已退役链路。
- 已核对实际会话产物:Responses 原生 `history` 已持久化 `reasoning` / `reasoning_text`,当前修复补齐 `reasoning_text` 提取,并在策划会话恢复时回填最近一轮 reasoning。
## 背景与现状
- `platform-llm` 当前只向上层提供正文累计值、正文增量和结束状态。
- Chat 兼容响应中的 `reasoning``reasoning_content` 以及 reasoning content part 会被正文提取器过滤。
- Responses 响应中的 reasoning 类型 output item 也不会进入独立的上层字段。
- 策划 Agent 已经预留 `DesignEvent.reasoningText``planningV2Reasoning` 和默认折叠 UI,但 Provider 解析链没有产出数据,因此折叠区通常不出现。
- GameAgent 当前只消费 `delta_text``accumulated_text``finish_reason`,没有消费策划 Agent 的 `reasoningText`
## 目标
1. 为 Provider 流式响应增加独立 reasoning 增量和累计通道。
2. 为非流式终态响应提供独立 reasoning 字段。
3. 支持 Responses 和 Chat 兼容协议的 reasoning 解析。
4. 仅由策划 Agent 显式启用 reasoning 捕获和 UI 转发。
5. 保证 reasoning 不进入用户可见正文、工具调用参数或 GameAgent 消息流。
6. 在无 reasoning、reasoning 解析异常、重试和工具调用共存场景下保持可恢复行为。
## 非目标
- 不改变 GameAgent 的正文展示、工具调用、`<think>` 过滤和运行时状态语义。
- 不把 reasoning 自动拼接到 `delta_text``accumulated_text` 或正式 assistant 消息。
- 不把 reasoning 作为新的业务消息类型写入策划会话历史。
- 不新增通用 reasoning UI,不改造 Direct/Codex 的过程卡展示。
- 不修改 Provider 请求模型、推理档位或 token 预算。
- 不为 reasoning 增加新的 SpacetimeDB 表、公开 API 或持久化 schema。
## 受影响模块与边界
### Provider 共享层
`server-rs/crates/platform-llm` 负责协议解析和流式累计:
- `LlmStreamDelta` 增加 `reasoning_delta``accumulated_reasoning`
- `LlmRunResponse` 增加终态 reasoning 字段。
- `LlmRunRequest` 增加默认关闭的 reasoning 捕获开关。
- 正文提取继续排除隐藏 reasoning partreasoning 进入旁路字段。
- reasoning 解析失败只丢弃 reasoning,不影响正文和工具调用。
### 策划 Runtime
`apps/ai-game-creator-shell/src-tauri/src/agent/design_runtime.rs` 仅在策划专用请求中打开 reasoning 捕获:
- 流式 reasoning 更新映射到已有 `DesignEvent.reasoningText`
- 正文继续使用已有 `text` 事件。
- 新回合、重试、项目切换和请求失败时清理旧 reasoning。
- debug 记录与正文记录分开,内容受现有 debug 开关和长度限制约束。
### 其它调用方
GameAgent、Agent Interaction、Direct/Codex 适配层和通用 runtime 继续只读取正文字段。新增 reasoning 字段默认为空,不改变这些调用方的业务判断。
### 前端
复用现有 `ProjectSupervisorView``designReasoning` 和默认折叠 `<details>` 展示。只补事件生命周期和状态清理,不新建平行组件或平行状态协议。
## 分步实施方案
### 第一步:冻结共享契约与兼容开关
明确字段语义、空值语义和捕获开关:
- reasoning 字段只表示 Provider 返回的内部推理内容,不代表用户正文。
- 捕获开关默认关闭;未启用时新增字段为空。
- 正文、工具调用、finish reason 和 Responses 原生 output 的现有语义保持不变。
- 该步只更新规范、类型定义和构造点,不接入策划 UI。
验收重点:所有现有 Rust 构造点可编译,GameAgent 现有调用仍只依赖正文字段。
### 第二步:实现 `platform-llm` 协议解析
分别补齐:
- Responses reasoning 增量事件;
- Responses 终态 reasoning output item / summary
- Chat `reasoning``reasoning_content` 和 reasoning content part
- 正文与 reasoning 的独立累计;
- reasoning 与正文、工具调用同时出现时的顺序和去重;
- reasoning 解析失败时的降级行为。
Responses 的原生 output 仍按当前方式保留,用于后续 Responses 会话回放;新增 reasoning 字段只用于上层展示和调试消费。
验收重点:正文永远不含 reasoning;无 reasoning 的响应与当前行为一致。
### 第三步:补齐共享适配层并锁定 GameAgent 不变
更新 `LlmStreamDelta` 构造点、适配器和测试辅助函数,使它们为新增字段提供空值。检查并锁定:
- GameAgent 正文流不读取 reasoning
- 工具调用判断不读取 reasoning;
- Direct/Codex 过程卡不显示 reasoning
- 通用 response stream 过滤逻辑不因新增字段改变。
验收重点:现有工具调用、正文流式、Direct 和 Agent Interaction 测试无行为回归。
### 第四步:接通策划 Runtime 与现有 UI
仅在策划 Agent Provider 请求中启用捕获开关:
- 收到 reasoning 增量时发出独立 `reasoningText`
- 收到正文增量时继续发出原有 `text`
- 重试时替换同一回合的临时 reasoning,不残留上一 attempt
- 正式回合结束后保留本回合展示,下一回合开始时清理;
- UI 默认折叠,展开后显示累计 reasoning,不影响正文滚动和输入。
验收重点:策划 Agent 能看到独立 reasoning,正文气泡不重复、不混入推理文本。
### 第五步:完成回归、文档和验收证据
形成逐条证据矩阵,至少覆盖:
- Responses reasoning 增量和终态;
- Chat reasoning 字段和 content part
- 正文与 reasoning 分离;
- reasoning 与工具调用并存;
- reasoning 解析失败降级;
- 无 reasoning 兼容行为;
- 策划 Runtime 事件映射和 UI 生命周期;
- GameAgent 正文与工具调用回归。
## 第五轮验收证据
| 验收面 | 证据 | 结果 |
| --- | --- | --- |
| Chat / Responses reasoning 解析 | `cargo test --manifest-path server-rs/Cargo.toml -p platform-llm` | PASS152 个单元测试;含字段、content part、SSE 增量、终态快照和正文隔离 |
| reasoning 与工具调用共存 | `responses_response_captures_reasoning_alongside_tool_call`、既有 Chat/Responses 流式工具测试 | PASS |
| 默认关闭与请求兼容 | `run_request_defaults_to_openai_responses_api_kind``reasoning_capture_switch_does_not_change_provider_request_body` | PASS |
| 策划 Runtime 生命周期 | `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bin genarrative-ai-game-creator-shell design_runtime` | PASS10 个测试;含事件映射、history 隔离、重试清理和失败清理 |
| GameAgent / Direct/Codex 正文回归 | `cargo check --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --tests` 与现有 response stream / direct tests 编译 | PASS;新增字段未进入正文消费路径 |
| 前端与文档门禁 | `npx tsc -p apps/ai-game-creator-shell/tsconfig.json --noEmit``npm run check:encoding``npm run check:doc-index``git diff --check` | PASS |
| 格式门禁 | `cargo fmt --all --manifest-path server-rs/Cargo.toml -- --check`、AGC Tauri 同命令 | PASS |
真实 Provider、浏览器运行时 smoke 和 `check-config.mjs` 的 Windows 私有 DACL 路径本轮未验证;前者需要凭据和运行环境,后者受当前沙箱权限限制,不能据此扩大验收结论。
## 契约与持久化策略
- 不修改 HTTP API、OpenAPI、SpacetimeDB schema 或生成绑定。
- 不新增正式持久化字段;策划会话仍保存既有对话和 Responses 原生 output。
- reasoning 捕获开关属于 Provider 请求的内部调用语义,默认关闭,不改变已有请求的默认指纹和展示行为。
- reasoning 不作为下一轮普通用户可见正文回灌;Responses 原生 output 的恢复语义保持现状。
## 失败、重试与恢复
- reasoning 解析失败:保留正文和工具调用,reasoning 字段置空或保留已累计部分。
- Provider 瞬态重试:reasoning 与正文使用同一回合、同一响应槽,新的 attempt 替换临时值。
- 流中断:沿用现有 Provider 错误和策划会话恢复规则,不把未完成 reasoning 误判为正式消息。
- UI 刷新或项目恢复:只从当前事件/状态恢复 reasoning,不隐式唤醒 Provider。
## 风险与回滚点
| 风险 | 控制措施 | 回滚点 |
| --- | --- | --- |
| 共享结构体新增字段导致构造点遗漏 | 先补齐所有构造点和编译检查 | 回退共享字段提交 |
| Provider 把 reasoning 混入正文 | 保留独立提取器和正文过滤测试 | 关闭 reasoning 捕获开关 |
| Responses summary 事件重复累计 | 以增量事件为主,终态仅做快照/兜底 | 关闭对应事件解析 |
| GameAgent 意外展示 reasoning | 捕获默认关闭,调用方只读正文字段 | 回退策划开关,不影响共享解析 |
| 重试残留旧 reasoning | 按回合和响应槽清理/替换 | 回退 UI 事件消费 |
## 验收命令
代码实现阶段按里程碑执行,不在本计划阶段运行业务测试。预计命令:
```text
cargo test -p platform-llm
cargo test -p ai-game-creator-shell
npm run typecheck
npm run check:encoding
git diff --check
```
文档阶段已要求补充运行:
```text
npm run check:doc-index
npm run check:encoding
git diff --check
```
## 第六轮定位:历史 reasoning 位置修复
持久化 Responses history 中,reasoning 不一定紧邻可见 `message`:一次 Provider 响应可能先产生 reasoning 和多个 `function_call`,下一次响应才产生正文。原实现遇到下一段 reasoning 就提前结束上一段,无法绑定到 `session.messages` 中的正确 assistant 响应,前端遂把无 `messageId` 的条目统一追加到列表底部。
修复方式是按每个用户回合分别收集:
- `session.history` 中 reasoning 的响应顺序;
- `session.messages` 中 assistant 消息的响应顺序。
两者按顺序绑定,允许 reasoning 跨越 tool-only Responses;同一正文对应多段 reasoning 时前端合并显示,仍使用默认折叠的单个思考区域。只有没有任何可见 assistant 消息的异常历史才保留为底部 orphan。
验证:新增 tool-only Responses 顺序回归测试,策划 Runtime 定向测试 11/11 通过;前端聚合逻辑保留历史多段 reasoning 的顺序。
## 当前状态与下一步
当前已完成 Provider 解析、策划 Runtime 生命周期、历史 reasoning 恢复及响应顺序归属修复;待本轮提交后继续按定向回归结果推进后续验收。字段语义、默认关闭策略、Responses 事件覆盖范围和 reasoning 不进入普通上下文的约束保持不变。
@@ -0,0 +1,29 @@
# 对话回合唯一投影
- Version: 2
- Status: implemented-awaiting-runtime-acceptance
- Date: 2026-09-16
- Parent Spec: ../../technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md
## 范围与评审
单里程碑修复回合展示和流写入的一致性;补充终态重进恢复、用户发送时间和完成后的过程折叠。评审确认:活动快照及 Direct 事件拥有生命周期,Provider 回放不创建 client 回合;JSONL 信封可选时间字段不污染原始 item,无须数据库或旧数据迁移;最终回复沿用 Runtime 的最后 assistant item 合同,失败提示不折叠。没有身份的旧记录不得做位置猜配。
## 验收
1. 一个 turn 只有一个呈现入口,用户消息不丢失。
2. item 增量、完成、持久化和回读保持相同身份与固定顺序。
3. 工具输入输出保留,重复快照不重复渲染。
4. TypeScript、最小 Cargo 检查、编码和 diff 检查通过;用户要求不运行测试,实机新回合/重开/分页验收待确认。
5. 已结束回合重进不显示提交中,真实运行回合可恢复;跨项目/新回合迟到快照无效。
6. 用户消息时间可刷新恢复,旧无时间记录不造值;完成后中间正文和工具统一折叠,最终回复及失败提示保持可见。
依赖:既有项目历史和 v1 turn-stream / tool-calls DTO。未完成真实 UI 验收前不进入其它里程碑。
## 当前证据
- 定向 TypeScript 类型检查、`cargo check --locked --bin genarrative-ai-game-creator-shell`、编码检查、文档索引检查、`git diff --check` 通过。
- 已补充回合归属、分页、重复快照、无流回退及 writer 完成/切段、持久快照单调性/跨回合裁剪用例;按用户要求未执行测试,不能作为已通过凭证。
- 静态自审确认视图只剩统一回合列表,不再存在 mapped/unmapped/live 三个回合流出口;失败提示使用稳定 failure 身份。
- 真实新回合、历史重开、分页、失败/中断、工具展开输入输出仍待重启原生客户端后验收;仅本地提交,不推送。
- 本次增量已完成生命周期来源收敛、信封发送时间和完成过程折叠;定向 TypeScript、ESLint、Cargo check、文档索引通过。新增时间幂等/旧记录、终态分类及内容分区用例但未运行;主页进入项目、重新发送/切项目竞态和自动折叠仍待原生实机验收,仅本地提交,不推送。
@@ -0,0 +1,23 @@
# 文档素材 Markdown 预览
- Version: 1
- Status: implemented-awaiting-validation
- Date: 2026-09-16
- Parent Spec: `docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md` 的“文档与代码素材预览”。
## 范围与评审
单里程碑:在已有资源画布和 Markdown 渲染链路中增加只读文档/代码预览。
边界评审:沿用现有权限、scope 与缓存身份;不改 Rust/DTO/持久化数据;代码只在详情请求读取,卡片预取不变;未知语言与大文件退回普通代码,不执行源码。无前置里程碑。
## 验收
- 文档/代码有预览入口,其它素材无此入口;弹窗支持关闭与滚动。
- Markdown 标题、列表、表格可读;代码有语言高亮,反引号、缩进、空行不丢失。
- 代码可见性预取仍不读文件;显式详情复用现有队列。
- 加载、空文件、错误与切换身份不显示错误素材的正文。
- AGC 类型、编码、文档索引与 diff 检查通过。按用户此前约束不运行测试,补充回归用例,实机验收单列为待验证。
## 验收状态
已实现入口、弹窗、队列按需读取、Markdown 代码围栏与高亮;已通过 AGC TypeScript(含新增预览测试文件的静态类型检查)、编码、文档索引、CSS 语法和 diff 检查。回归用例未执行;原生客户端读取、弹窗焦点/滚动和视觉验收仍待验证,不进入下一里程碑。
@@ -2,7 +2,54 @@
> 用途:记录已经确认、会影响后续开发的长期技术/产品/协作决策。短期讨论不要写在这里。
> 当前口径:历史条目的旧路径、旧版本和已退役对象只用于追溯,不构成现行实现依据;如与当前代码或 `docs/README.md` 冲突,以当前代码和最新专题文档为准。
## 2026-09-16 图标图集自动拆图上限提高到 256
- 背景:AGC 图标图集自动连通域识别在一次生成中识别出 86 个区域,原有 64 片上限在后处理阶段阻断了请求;该上限同时影响 api-server 自动 / 手动切片、SpacetimeDB 批量落库和统一生成结果 item 数量。
- 决策:将可输出独立切片上限统一提高到 `256`;统一生成结果最多 `258` 个 item256 个切片加 provider 原图和透明整图)。保持原始连通域 `4096`、总裁剪像素、CPU / 内存 admission、并发上传和处理时限不变。
- 边界:超过 256 仍按现有 `output-slice-limit-exceeded` / `sliceWarning` 语义失败关闭切片写入;自动路径保留可信整图,手动路径继续在持久化前返回错误。
- 验证:平台切片器、api-server 警告映射与 payload、SpacetimeDB 结果 / 批次校验均覆盖 256 成功边界和 257 溢出边界。
## 2026-09-14 生成进度面收敛为「常驻可折叠任务侧栏」;提交即关面板、阶段文案只归侧栏;定位动作终局化
- 背景(验收人在真机上连报三条):① 提交按钮上渲染了后端 `phaseDetail`,「生成图片」的主按钮变成写着「排队中。」的状态胶囊;② 提交后提交面板不关、一直占着屏幕等生成,用户原话「不要显示排队中,点生成直接把窗口藏起来啊,你留个窗口意义何在」;③ 任务进度面是一个工具条按钮 + 非模态浮层,跟网页端美术画布的任务侧栏不是一个形态,用户原话「你把美术画布的照抄过来都不会吗」;④ 「定位到素材」点了没反应,提示条永久停在「正在定位生成的素材…」。
- 决策(提交面板):**点「生成」即同步关闭面板**——不等 IPC、不等排队、不等生成;面板内**不出现**任何阶段文案(主按钮文案恒为动作名)。**只有「点击瞬间就失败」**(后端校验 / 权限拒绝 / start IPC 立即报错)才自动重开面板并带回草稿与原因;**受理之后才失败**只在任务侧栏把该任务收口为失败 + 原因,不重开面板。关闭 ≠ 取消(请求挂在任务与项目内账本上,不挂在面板生命周期上)。
- 决策(进度面形态):改为**常驻画布的可折叠任务侧栏**(对齐网页端 `ImageCanvasTaskSidebarView`)——展开是两个分栏「排队/生成中」与「已完成」(各带条数,「已完成」封顶 20 条 + 提示「仅显示最近 N 条」),关闭入口只保留头部那一枚 ×(底部重复的关闭按钮与其 border-top 分割线已删除);折叠即整块让出画布、**不留贴边把手**;每项显示状态徽标 / 后端阶段文案 / 已耗时(前端 1s 计时)/ 素材名 + 「定位到素材」;侧栏非模态(不铺遮罩、不做焦点陷阱、不进模态遮挡判据),工具栏入口按钮是**唯一**开合口、常驻(资源管理 / 运行两个页签都在)并显示 `生成任务 · N`,提交受理后自动展开。**原先的非模态浮层形态已删除,不留平行入口。**
- 决策(侧栏失去焦点即收起):展开时挂 document 级 `pointerdown` 捕获监听,点在侧栏内部与工具条那枚开合按钮以外的地方即收起。两处必须排除——**侧栏内部**(点任务卡、点「定位到素材」不能收起侧栏)与**开合按钮本身**(它自己负责 toggle,若也被判成「点外部」就会先收起再被 toggle 打开,表现为按钮失灵;按钮带 `data-resource-generation-task-toggle` 标记供排除)。用 `pointerdown` 而不是 `click`:画布空白处的左键 pointerdown 会 `preventDefault()`document 上的 click 收不到那一次点击。
- 决策(收起动画):收起不能瞬间卸载——进场有动画而消失没有,观感上是"闪一下没了"。做法是**组件自己留一帧播退场**:`open` 变 false 后进入 `leaving`,根节点挂 `is-leaving``…-leave`(与进场同向反向,160ms`pointer-events: none`),播完(或减动效偏好的 0ms 定时)才 `setPhase('idle')` 卸载。时长在组件与 CSS 两处各写一次,**必须一致**(组件导出 `RESOURCE_CANVAS_ASSET_GENERATION_TASKS_LEAVE_MILLIS` 并在用例里钉住)。退场期间**沿用收起前那一份列表**(`lastRenderedRef`):宿主会在同一帧里收起侧栏并把在途任务收口成已完成,直接吃新 props 会让退场动画里的内容跳一下;空态分支也必须读这份冻结快照,不能读实时 `ordered`
- 决策(定位必须让素材真的可见):`handleResourceSelect` 只改选中、不会移动画布,而这张画布是 **transform 平移**的、资源卡不在任何滚动容器里——`card.scrollIntoView()` 碰不到滚动祖先,卡在视口外时"定位过去了但依然见不到素材"。所以聚焦链在选中之后必须**显式把画布视口居中到该卡**`centerResourceCanvasOnResource`:读该卡在当前位置表里的 `x/y``resourceCardSizeByResourceId` 的尺寸,按 `canvasSize / 2 卡中心 × scale` 求平移量,**缩放保持不变**,与 `ensureResourceBookContentVisible` 的既有口径一致:定位不改用户的缩放预期)。`scrollIntoView` 一并保留(栏目页仍有带滚动条的祖先)。
- 决策(侧栏位置):挂在画布**左侧、标题栏之下、工具栏之上**,宽 300px、**覆盖式**(不 reflow 挤窄画布视口)。理由:右侧已被「智能创作」对话面板占用、顶部是栏目标题栏、底部是栏目工具栏;覆盖式不触碰画布视口数学与资源卡排布,收起即完全让出画布。若产品要求「画布被挤窄」的 flex 兄弟列形态(网页端是那种),需要改 `game-resource-book-manager` 那段布局并单独排期。
- 决策(定位终局化):根因是聚焦 effect 的依赖全是画布自身状态,**手动点定位不改其中任何一项** → effect 不重跑、`pendingResourceFocusRef` 无人消费、提示条永久停在中转文案。修法:新增聚焦请求序号并加入 effect 依赖;handler 重写为「能定位就定位并选中;素材在别的栏目先切栏目;不在投影里给『素材已不在项目里 / 已登记但尚未同步』的结论;挂 intent 后推进序号 + **3 秒有界兜底**intent 被判 invalid 时也给『定位请求已失效』」,并修掉「已聚焦过」提前返回分支不清提示的同类问题(自动落卡那条链同源)。
- 影响范围:`src/features/resource-canvas/{ResourceCanvasAssetGenerationPanelView.tsx,ResourceCanvasGenerationPanelView.tsx,ResourceCanvasAssetGenerationTasksPanelView.tsx,resourceCanvasAssetGenerationTaskModel.ts,resourceCanvasAssetGenerationQueue.ts}``src/view/project-development/index.tsx`,测试 `tests/{resourceCanvasAssetGenerationBackgroundClose.test.tsx,resourceCanvasAssetGenerationTasksPanel.test.tsx,resourceCanvasBottomToolbar.test.tsx,appSurface/project-development.suite.ts}`。**未改** IPC 形状与 Rust 生成通道、未改本地排队语义(仍单条在途)、未改 `packages/**`
- 验证方式:`appSurface.test.ts` 439 passed、定向 7 文件 93 passed、侧栏用例 7 passed;变异验证(均已实测):① 提交后不关闭面板 → 面板用例 `expected "spy" to be called 1 times, but got 0 times` 与 AppSurface `expected <section …> to be null` 红;② 去掉即时失败重开 → `Unable to find role="dialog" and name "生成 UI 设计图"` 红;③ 去掉提交后自动展开 → `Unable to find an accessible element with the role "region" and name "生成任务"` 红;④ 折叠顺手清空任务列表 → `expected '0' to be '1'` 红;⑤ 去掉聚焦请求依赖 → `expected null not to be null`(卡片从未被选中)与 `expected <span></span> to be null`(提示条仍停在中转文案,即用户报的现象)红。
- 关联文档:[栏目画布底部工具栏入口矩阵](../../technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md)、[项目开发工作台 PRD](../../prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md)、[踩坑记录](pitfalls.md)。
## 2026-09-14 放开 AGC 手工图片生成的本地并发:durable 输出槽身份改为「精确动作指纹」
- 背景:AGC 手工图片生成的 durable 输出槽身份是 `run_id = slot-<sha256({outputPath, requireSlices})>`,而工具栏除「图标规范」首次外 `outputPath` 恒为 `null``requireSlices` 恒 false → 同项目所有图片类生成共用一个槽,第二条并发请求在任何远端 POST 之前就被拒(`external_generation_state.rs` 的 singleflight 报「durable 图片生成输出槽已有请求执行中」)。验收反馈里的高优问题(生图期间不能退出、也不能在生成 A 的过程中生成 B)既要前端可退出,也要后端具备并行能力。
- 决策(槽身份 = 精确动作身份):`run_id = slot-<sha256(动作身份材料)>`,材料为 `prompt / output_path / aspect_ratio / image_size / asset_kind / asset_label / replace_existing / require_slices``canvas_generation.rs`)。不同 prompt 或素材名 → 不同槽 → 不同进程锁键与不同 `.lock` 文件 → 可同时在途。**不用随机 uuid**:随机身份会让「同一精确动作重放」落到新路径,必须再造一层 action→ledger 索引才能保幂等;用动作指纹让「槽身份 ≡ 精确动作身份」,路径查找即幂等查找。
- 决策(幂等不变):同一精确动作 → 同一路径 → 命中已有 prepared/accepted 账本并复用原 `idempotencyKey` / `operationId`,不二次 POST;相同动作并发仍被拒的既有语义保持。
- 决策(旧槽账本最小懒迁移):旧槽账本形状可读、不 panic、不 fail-closed;在 durable guard 之后、任何远端 POST 之前,**仅当**旧槽账本的 `agentId / runId / actionFingerprint` 与本次精确动作一致时,把它迁移到新路径(保留 `idempotencyKey` / `operationId` / 状态)并删除旧文件;属于其他动作的旧账本一律不动。旧「固定槽」(`run_id == agent_id`)账本的 fail-closed 拒绝保持原样。
- 已知边界:`slice_count` **不进**身份(保留升级前粒度,也是旧账本迁移可行的前提)→ 仅切片数不同的两条图集请求仍共槽、第二条失败关闭;当前所有 standalone 槽的生产调用方都把 `slice_count` 传成 `None`(唯一能传 sliceCount 的是 agent 工具通道,它不走 standalone 槽),该边界当前不可达,但**缺负向用例**
- 前端口径:本批**仍保留单条在途的前端排队**(提交节流),真并行派发需要并发收口设计(配对读 + manifest CAS + 聚焦意图互不覆盖),留待下一批。
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/agent/generation/canvas_generation.rs``.../external_generation_state.rs`。**未改** `/api/external/v1` 契约 / OpenAPI / DTO,未改 `recovery_scan.rs`(身份白名单与孤儿清理语义不变),账本 schema 仍是 v3、字段集不变,只改 `runId` 取值来源。
- 验证方式:新增 `standalone_generation_binds_each_exact_request_to_its_own_stable_slot``distinct_standalone_actions_hold_independent_durable_output_slots``concurrent_distinct_standalone_generations_both_succeed_with_one_post_each`(端到端:两条 `outputPath=None` 的不同动作要求两条 POST 同时到达,各自 poll → read-url → 下载 → 落盘)、`legacy_output_slot_ledger_is_adopted_by_the_same_exact_action_only`。变异验证(已实测):把 `run_id` 退回旧公式 → 4/4 红(含「durable 输出槽身份必须等于该精确动作的身份」与并发用例的「任何远端 POST 前拒绝并发请求」);把懒迁移短路 → 旧账本用例红。定向 `agent::generation::` + `recovery_scan` 95 passed、`direct_runtime media` 195 passed。
- 关联文档:[栏目画布底部工具栏入口矩阵](../../technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md)、[踩坑记录](pitfalls.md)。
## 2026-09-14 「素材类型」从「编辑素材标签」面板拆成独立入口
- 背景:2026-09-11 的决策把类型选择器放进「编辑素材标签」面板(与标签同一次保存、同一条写入路径,不在工具条另开入口)。真机使用暴露两个问题:① 那排 chip 在弹窗里没有任何标题,读不出是什么;② 类型改动**没有自己的提交动作** —— 点 chip 只写本地 state,落盘发生在底部「添加」上(那是标签语义的按钮),只选类型后直接关弹窗会静默丢失。
- 决策(入口独立):新增 `ResourceTypePanel`(标题与 `ariaLabel` 均为「设置素材类型」),**选中即落盘**(一次动作一步完成),`category` 传用户选中值、`tags``gameCreationAppAssetTags(asset)` 的落盘原值。入口两处:选中卡浮动工具条「素材类型」按钮 + 信息浮层「分类」行的「设置」。
- 决策(选项形态,真机反馈后修订):选项区**必须是纵向单选列表**,一行一个(容器 `role="radiogroup"`、每项 `role="radio"` + `aria-checked`,选中态与读屏共用 `aria-checked` 并由 CSS 直接驱动;roving tabindex + 方向键移焦点、**Enter/Space 才落盘**)。真机上第一版用了共享 `PlatformSegmentedTabs``columns="threeToSix"`)→ 6 个选项挤成一行互相叠字,验收人原话「做成列表,而不是全都一条」。**有意偏离 APG**:方向键不顺手选中——本面板「选中 = 一次 CAS 写盘 + 宿主收窗」,方向键即选中会让浏览 6 个选项变成连环写盘、第一次按键就关窗。行骨架复用共享 `PlatformNavigableListItem`(未复制共享 UI、未改 `packages/**`);列表 `max-height: min(320px, 40dvh)` + 独立滚动,标题/素材名/错误提示不滚,行高 44px 移动端优先。这一版值得后续抽成 `packages/shared``PlatformRadioList`(或给 `PlatformSegmentedTabs` 加 vertical 档),本批按边界未做。
- 决策(标签面板去掉类型控件):「编辑素材标签」面板删除 chip 与 `categoryChoice` 分叉,保存时 `category: gameCreationAppAssetPersistedCategory(asset)`;「没碰过分类就回传落盘原值」这条不变量改为**结构性保证**(面板里根本没有类型控件),两条对照用例迁到新面板并保留。
- 影响范围:`src/view/project-development/{ResourceTypePanel.tsx,ResourceClassificationPanel.tsx,ResourceInfoPanelView.tsx,resourceCanvasInfoModel.ts,index.tsx}``src/features/project-workspace/resourceTypePanel.css``tests/{resourceTypePanel.test.tsx,resourceClassificationPanel.test.tsx,projectResourceLiveIntegration.test.tsx,appSurface/project-development.suite.ts}`。不改 `update_local_project_resource_classification` 的入参形状与 CAS 口径、不改读时自愈语义、无后端与 schema 变更。
- 验证方式:`resourceTypePanel.test.tsx` 新 12 条 + `resourceClassificationPanel.test.tsx` 19 条 + `appSurface.test.ts` 431 passed。变异验证(已实测):新面板 `category` 改回回传落盘原值 → 「改类型生效」用例红;标签面板改用显示口径 → 对照用例出现 `- "category": "unclassified" / + "category": "ui-interaction"`;去掉浮层判据里的新 state → 点外部串台用例红。
- 关联文档:[AGC 资源工作台 V3 端到端验收用例](../../technical/【测试用例】AGC资源工作台V3端到端验收-2026-09-11.md) 的 S12。
## 2026-09-14 同步命令 `generate_local_project_asset` 退役为「仅测试调用」
- 决策:图片类生成接线改为 `start_local_project_asset_generation` + `list_local_project_asset_generations` 后,同步命令 `generate_local_project_asset` **已无生产调用方**,只剩 `src-tauri/src/tests/project.rs` 的三条集成用例与 `commands.rs` 的自身单测在调它;因此登记进 `scripts/check-config.mjs` 的 native-only 白名单(该门禁有「App invoke 与白名单互斥」断言,谁重新给它接调用方就必须同时删掉这条白名单项)。
- 待办:它是**注册中的可调用 IPC**,一旦被将来代码调用就是一条绕过任务账本、单次阻塞最长 35 分钟的并行生成路径。下一批次应删除它,或改为转调 `start_local_project_asset_generation`(连带迁移那三条集成用例)。
- 关联文档:[栏目画布底部工具栏入口矩阵](../../technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md)。
## 2026-09-14 AGC 壳 Rust 套件按「一片一 job」拆分,客户端 Rust 关键路径压到 7 分钟以内
- 背景:`AI game creator shell Rust tests` 是客户端 CI 的关键路径(run 2097 实测 15 分 27 秒)。拆开来看:前置 5 分 30 秒(checkout 10s + `npm ci` 2m45s + Cargo fetch 2m35s)、编译 1m39s、**AGC 壳 bin target 的 2466 条单测串行 507s**、`agent-run` smoke 51s。这 2466 条全在 `apps/ai-game-creator-shell/src-tauri` 的 bin target 里,一条 `cargo test … -- --test-threads=1` 跑完。
@@ -15,6 +62,15 @@
- 影响范围:`.gitea/workflows/project-ci.yml`(十一个 job)、`scripts/check-native-shells.mjs`(分组由五个到十个:新增 `agc-rust-crates``agc-rust-shard-1..4``agc-rust-smoke`,移除 `agc-rust` 与随后的 `agc-rust-shell`)、根 `package.json``scripts/project-ci-workflow.test.ts`(新增纯 cargo job 免 `npm ci`、分片运行器覆盖校验、crate 级 job 预热顺序断言)、开发运维文档与共享记忆。本仓库不把 Project CI 的 context 配成 `master` 分支保护的合并必需检查(2026-09-14 复核),合并前由人工确认结果,因此 job 拆分/改名不需要同步分支保护设置。
- 验证方式:`npx vitest run scripts/project-ci-workflow.test.ts`;分片运行器本地以 `agent-runtime-core`7 条 → 2/2/2/1)与 `platform-llm`146 条 → 49/49/48)验证分片、`--exact` 与片 TMPDIR 隔离,负例 `--shard-index=5` 立即失败;`node scripts/check-native-shells.mjs --groups=contract` 回归。预期每个分片 job 收敛到 5 分钟以内(前置约 1 分 30 秒 + 编译约 1 分 39 秒 + 约 617 条用例)。
- 关联文档:[开发运维](../../【开发运维】本地开发验证与生产运维-2026-05-15.md)、[踩坑记录](pitfalls.md)。
## 2026-09-14 AGC 资源画布改为「手动整理」:新素材不再自动重排,整张重排只由「整理画布」发起
- 背景:生成一张新素材会让整张资源画布重排。两个 layout hook 都把 `rederiveAutomaticPositions` 打开(type 侧无条件 `true`dependency 侧长期等于 `resourceGraphReady`),而该开关的语义是「每次资源协调签名变化就丢掉全部 `manuallyPlaced=false` 坐标、按当前资源与拓扑整体重算」;新增一张素材必然改签名,于是既有自动卡全部跟着挪位,用户刚记住的位置就没了。画布上也没有任何显式整理入口(`复位资源视图` 只复位视口)。
- 决策一(默认口径):两个 mode 的 `rederiveAutomaticPositions` 固定 `false`——画布默认只补新卡,不动任何既有坐标(`preserve`)。整张重排改为显式动作:资源工具条动作区新增**独立的**「整理画布」动作按钮(**不在**「资源排列方式」这个 `role="group"` 内——它是一键动作,不是第三种排列方式;真机上第一版塞进排序 tab 组里被验收人指出「整理画布的按钮独立出来,不要塞到那个里面」,已移出;第二轮又被指出「不要放在最右边」,所以**最终位置固定在「生成素材」之后、「管理未完成编辑」之前**(紧邻同类资源动作、在排序组左侧,不做这一行的行尾按钮——行尾会被读成「针对整个工具条」的动作)),调用 hook 新暴露的 `rederiveNow()`,复用既有写队列与 sidecar 写回链路,只把策略换成 `rederive`;用户可见反馈继续用既有 `resourceLayoutNotice`(成功即「布局已保存」)与 `resourceLayoutSaving`,不新增状态位。重算结果与当前坐标一致时**不落盘**(沿用既有 `changed` 门):已经整齐的画布按一下不该白推进一次 CAS / revision,关系图 `producerMappingTruncated` 时更不该把一份来自不完整关系图的自动布局写进 sidecar——既有用例 `keeps trusted truncated-graph depths through the workbench without persisting a flat automatic layout` 就是钉这条。
- 决策二(依赖图首次就绪的那一次):`dependencyDepth` 仍要在关系图就绪后按最终拓扑排一次列。这一层不再靠「让布尔长期为真」,而是按**项目作用域的一次性 flag**(`dependencyRederiveScopeRef`):关系图就绪且该侧 sidecar `ready` 的那一刻调用一次 `rederiveNow()`,之后一律 `preserve`;切排序 tab 不重新武装,切项目才重新记一次。
- 决策三(生成后自动聚焦):新增 `seenManifestAssetIdsRef` + effect,按 `manifest.assets` 的**新增 id**(不是 diff 位置、也不是文件名)把新卡交给既有 `pendingResourceFocusRef` + `advanceFocusGeneration()` 裁决链。首次打开项目 / 切项目只登记基线、不聚焦;重命名不改 id、天然不触发;已有指向同一资源的聚焦意图时不重复挂(显式生成链路在提交时就已挂好)。被搜索条件挡住时继续复用既有的「清除搜索并定位」提示与动作。
- 影响范围:`apps/ai-game-creator-shell/src/view/project-development/useProjectResourceCanvasLayout.ts`(写意图增 `rederive` 标记、写策略分支、`rederiveNow`)、`.../index.tsx`(两个 hook 配置 + 一次性重派生 effect + 新素材聚焦 effect + 「整理画布」按钮)、`apps/ai-game-creator-shell/tests/resourceCanvasManualLayout.test.tsx`。不改 `manuallyPlaced` 语义、不改 sidecar 的 CAS / revision 协议与字段形状、不改卡片尺寸模型、不改「搜索不重排」合同、不改 Rust 资源图与 `dependencyDepth` 权威。
- 验证方式:新增 `resourceCanvasManualLayout.test.tsx` 8 条:新素材入库后除新卡外坐标逐值不变、依赖侧首次就绪重算一次后同样不再重排、新素材自动聚焦、被搜索挡住走既有提示与动作、「整理画布」按 `rederive` 重算并保留手动坐标且给出一次可见反馈、已经整齐时再按一次不产生第二次落盘、首次打开与切项目都不聚焦。变异验证(均已实测):① type 侧改回 `true`、dependency 侧改回 `resourceGraphReady` → 3 条红;② 去掉新素材基线的首次登记 → 3 条红(既有卡被当成"刚生成"选中);③ 去掉 hook 里的 `rederive` 写策略分支 → 3 条红;④ 把显式整理改成强制写回(去掉 `changed` 门)→ 既有「截断关系图」用例红。
- 关联文档:[踩坑记录](pitfalls.md)、[项目开发工作台 PRD](../../prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md)。
## 2026-09-14 客户端 CI 按门禁组拆成三个 jobAGC 的 web / rust 两段并行
@@ -56,7 +112,7 @@
## 2026-09-11 素材类型(功能分类)重新提供用户入口;资源卡角标改显示资源类型而非媒体类型
- 背景:`6bdc8bbd9` 把「分类与标签」面板收敛为纯标签面板,并明确记下「随之的事实是:**「用户手动设置 `category`」这项能力就此移除**」(本文件 2026-09-11 那条以「用户给出的目标样式截图里,这个面板标题是「编辑素材标签」…」开头的条目,其决策一行即该结论)。用户随后要求「用户可以自己变更素材类型」,并追加要求把资源卡右上角角标从"文件/媒体类型"(图片 / 视频 / 文档 …)改成"资源类型"(功能分类中文名)。现状核实:写入链路本来就是完整的 —— `update_local_project_resource_classification``src-tauri/src/commands.rs:2162`)→ `update_manifest_asset_classification_at``src-tauri/src/project/manifest.rs:1112`)已接受任意合法 `category` 并校验 6 个合法值,**本次不需要改 Rust**;缺的只有 UI 入口与角标口径。本条**只回收「面板不再编辑分类」这一项**,「面板标题仍是「编辑素材标签」、删除资源入口仍在选中工具条」等其余结论不变。
- 决策一(入口):类型选择器加进**「编辑素材标签」面板**(与标签同一次保存、同一条写入路径,不新增第二条命令、不在工具条另开第二个入口)。选择器复用 `GAME_CREATION_APP_ASSET_CATEGORIES` × `resourceReferenceCategoryLabel`,不新造第二套中文译名。
- 决策一(入口)【已被 2026-09-14「「素材类型」从「编辑素材标签」面板拆成独立入口」取代】:类型选择器加进**「编辑素材标签」面板**(与标签同一次保存、同一条写入路径,不新增第二条命令、不在工具条另开第二个入口)。选择器复用 `GAME_CREATION_APP_ASSET_CATEGORIES` × `resourceReferenceCategoryLabel`,不新造第二套中文译名。
- 决策二(两个口径的分叉,本次核心不变量):选择器**读显示口径** `gameCreationAppAssetCategory`(与画布栏目 `projectResourceAssetCategory` 同源,用户看到的选中项就是他看到的栏目);**写回**用 `categoryChoice` 区分用户是否主动选过 —— `null`(没碰过控件)回传 `gameCreationAppAssetPersistedCategory` 的落盘原值,非 `null` 写用户选的值。这条分叉同时满足"只改标签不漂移分类"与"用户选了就写用户的值",两个方向都有对照用例(见验证方式)。
- 决策三(角标):资源卡右上角角标改为**资源类型**,取值 `categoryLabels[resource.category]`(栏目与筛选共用的同一份文案),因此角标恒等于该卡所在栏目;媒体类型仍由卡面视觉(图片 / 视频 / 音频 / 文档摘要)表达。只改这一处渲染(`index.tsx``ResourceCard`),三处面(栏目画布卡、「所有资源」展开态卡、总览缩略摞上铺的卡)自动一致;`projectResourceTypeLabel` 保留给「资源管理面板」的「分类 · 类型」小字与总览摞分列,不再用于角标。
- 画布跟随链(核实结论,**无需额外迁移代码**):`useProjectResourceCanvasLayout``createResourceSignature` 已把 `resource.category` 计入签名,分类变化 → 签名变化 → `reconcileResourceCanvasLayout` 按新的 `section` 归并(`resourceCanvasSectionMapping.resolveResourceCanvasSection`:现行栏目值原样归到资源当前分类,x / y / `manuallyPlaced` 原样保留)→ 需要时写回 sidecar。卡片随分组落到新栏目。
@@ -156,6 +212,7 @@
- 背景:#211 要求 sidecar 满足当前用户独占、禁止继承的 DACL。新建文件会先继承父目录 ACE,生产路径把这种短暂不合格送进 UAC;`project.lock` 还在独占句柄上 harden。含空格项目路径上提权 ArgumentList 被拆开,修复以 exit 1 失败。GDD 审批改意见因此弹权限,V1 锁创建不会。
- 决策:`harden_new_game_creator_private_path` 只在本进程收紧 owner/DACL,失败则删除刚创建的对象,不 UAC 接管。项目锁先写再释放句柄再 harden,并用内容回读防换绑;UAC 仍只用于允许范围内的已有外人本对象。提权 helper 的 ArgumentList 改为一条按 Windows 规则加引号的字符串。
- 补充:逐级创建 `.agent``runtime``locks` 等目录时,即使祖先已有 `manifest.json`,刚由本进程创建的目录也必须直接走 owner/DACL 初始化,不能因 managed-path 判定进入 UAC;自动项目根目录同样在创建成功后立即本地加固。
- 影响范围:`config.rs` 的新建 harden 与提权命令行、`project/write_lock.rs` 的项目锁创建;不改变锁竞争、失效回收、Drop 删除,也不放宽 symlink / reparse / 外人本 fail-closed。
- 验证方式:Windows 定向测试覆盖 `Genarrative GameAgent\gameagent-*` 取锁与私有 DACL,以及带空格路径的 quoted ArgumentList。
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md``docs/project-memory/shared-memory/pitfalls.md`
@@ -307,6 +364,7 @@
- 背景:立项策划 GDD 批准后需要给用户一个进入做游戏的自然出口,产品决策改为点击按钮后直接开始建造。
- 决策:批准态 GDD 交付行提供“做成游戏”按钮。点击后读取当前项目的权威 `game/fast_gdd.md`,直接创建自动游戏工作区、导入 `text/markdown` 参考附件,并以固定建造指令自动启动 Direct Codex;不再回首页等待用户二次提交。该动作不复制原项目的 `approvedGddRef`、planning sidecar 或 approval receipt。
- 补充:策划项目切换到 GameAgent 时,`design_artifacts` 的新增或登记信息实际变化必须与一次项目 revision 推进配对;重复切换不重复推进,避免 manifest 已变化而 revision 仍停留在旧值,触发前端同 revision 清单冲突提示。
- 影响范围:AGC 前端 GDD 交付行与现有自动建项/附件导入/Direct Codex 链路;移除首页 RichInputArea 的 GDD 一次性预填链路;不新增 HTTP API、SpacetimeDB schema、迁移、OpenAPI 或正式构建绑定。
- 验证方式:批准态按钮直接创建工作区、导入附件、携带固定首条指令进入项目工作台且重复点击不重复创建的 appSurface 回归;类型检查、编码检查和 `git diff --check` 通过。
- 关联文档:`docs/technical/【技术方案】立项策划AgentFast GDD-2026-08-10.md`
@@ -8677,3 +8735,37 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 决策:复用判据收窄为**同一条写调用链(同一线程)重入**——按锁路径登记真实持锁线程,只有当前线程就是持锁线程时才返回 advisory guard;本进程其它线程的争用继续走有界等待与终态占用。自主游戏构建流水线的并行专家动作豁免保持不变;跨进程占用、残留回收、权限分类、等待预算和错误文案不变。
- 边界:锁定这些不变量的既有用例(`project_tools` / `command_runtime` / `parallel_actions` / `runtime_state` / `response_stream` / `direct_tool_bridge` / `ui_editor::persistence`)不得为了让锁语义通过而改写;用「同线程自持锁」模拟「另一个写者」的两条用例改为**在另一条线程持锁**,断言语义不变。同进程跨线程重入(持锁链在 `await` / `spawn_blocking` 后于其它线程再取锁)仍会等满预算,出现现场时按 2026-08-27 的既有处置改用 `*_locked` 入口,不放宽判据。
- 关联文档:[项目客户端占用锁收敛里程碑](../plans/【里程碑】项目客户端占用锁收敛-2026-09-14.md)、[踩坑记录](pitfalls.md)。
## 2026-09-14 AGC 图片类生成后台化:提交即返回 + 项目内任务账本 + 本地排队
- 背景:栏目画布图片类入口原先是一条同步 IPC `generate_local_project_asset`,一次调用最长等 35 分钟;提交期间两块生成浮层把 × / 遮罩 / Esc /「取消」全部锁死,用户既关不掉面板也看不到进度。远端图片类生成当时共用 single-flight 输出槽(`standalone_platform_art_generation_runtime_context``outputPath` 派生 `run_id`)——**同一批次内该槽身份已改为精确动作指纹,见本文件 2026-09-14「放开 AGC 手工图片生成的本地并发」条目。**
- 决策(后台化):新增 `start_local_project_asset_generation`(校验入参 → 落 `queued` 记录 → `tauri::async_runtime::spawn` 派发 → 返回任务记录)与 `list_local_project_asset_generations`(读回账本)。生成本身仍转调既有 `generate_platform_art_asset_with_options_at`(幂等账本、计费、下载校验、manifest/revision 登记、本地预览都不复制),同步命令 `generate_local_project_asset` **保留不动**
- 决策(账本落项目内):`.agent/runtime/asset-generation-tasks/tasks.json`,复用既有 agent runtime sidecar 读写原语(临时文件 + rename);非终态且不在本进程 live 集合里的记录在读取时收口为「上次运行中断」失败,不假装它还在跑;账本上限 50 条按创建时间淘汰。
- 决策(阶段文案归后端):`phaseDetail` 由 Rust 拥有(「排队中。」/「正在生成。」/「生成已完成。」/失败原因),前端面板与「生成任务」面板只渲染该字符串,不拼阶段、不做百分比。后端目前没有可播报的中间阶段(生成通道不暴露 job 的远端 `phaseDetail`),所以不伪造「正在处理。」这类前端文案。
- 决策(本地排队):第二条提交停在**前端本地队列**(`dispatched=false`,不调用提交 IPC,阶段显示本地排队的「排队中。」),第一条终态后由同一条循环自动补发。判据是「存在 `dispatched && 未终态` 的任务时不派发下一条」——**本批仍保留单条在途的前端排队**:AGC 本地槽此时已按精确动作指纹分槽(具备并行能力),但真并行派发需要并发收口设计(配对读 + manifest CAS + 聚焦意图互不覆盖),留待下一批;所以这里的排队是本批的**提交节流**,不再是「后端拒绝并发」的对应实现。
- 决策(面板可关 + 非模态任务面板):两块生成浮层在提交期间放开 × / 遮罩 / Esc,提交按钮旁给「后台运行并关闭」;**关闭 ≠ 取消**(表单的 `await` 挂在该任务的终局上,不是面板生命周期)。新增「生成任务」非模态浮层(不铺遮罩、不做焦点陷阱、**不进** `isResourceCanvasFloatingPanelOpen` / `resourceCanvasHostGenerationPanelOpen` 遮挡判据),入口按钮 `aria-label="生成任务"`;已完成的条目按 `assetId` 复用既有 `pendingResourceFocusRef` 聚焦链定位素材卡。
- 影响范围:新增 `apps/ai-game-creator-shell/src-tauri/src/asset_generation_tasks.rs`+ `main.rs` 注册)、`src/features/resource-canvas/{resourceCanvasAssetGenerationTaskModel.ts,resourceCanvasAssetGenerationQueue.ts,ResourceCanvasAssetGenerationTasksPanelView.tsx}`;改动 `ResourceCanvasAssetGenerationPanelView.tsx` / `ResourceCanvasGenerationPanelView.tsx` / `src/view/project-development/index.tsx`;测试改动 `tests/{resourceCanvasAssetGenerationBackgroundClose.test.tsx,resourceCanvasAssetGenerationQueue.test.ts,resourceCanvasAssetGenerationTasksPanel.test.tsx}`(新增)与 `tests/appSurface/project-development.suite.ts`(把「每个入口一次 `generate_local_project_asset`」改成 `start_local_project_asset_generation` + `list_...` 轮询桩,载荷断言逐字不变)。**未动**external v1 / OpenAPI、`packages/`、SpacetimeDB、音频入口的 pending-edit 账本语义、生成参数与 IPC 载荷字段名。
- 关联文档:`docs/technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md`(§4 / §4a / §8)、`docs/technical/【测试用例】AGC资源工作台V3端到端验收-2026-09-11.md`S11a / §7.3)。
## 2026-09-15 非 Suno 的 VectorEngine 能力切换到 Tiantoken
- 决策:新增本地私密环境变量 `TIANTOKEN_BASE_URL` / `TIANTOKEN_API_KEY`(图片 timeout 可独立配置),承载原 VectorEngine 的文本和图片;`VECTOR_ENGINE_BASE_URL` / `VECTOR_ENGINE_API_KEY` 仅保留给 Suno 背景音乐与 Suno 音效。编辑器 SFX V2 继续走 ElevenLabs。
- 实现边界:api-server 在创建状态时冻结 Tiantoken 配置,LLM、图片和旧版非 Suno 音频按该配置路由;Suno 的提交 / 轮询仍使用旧 VectorEngine 配置。旧 `vector_engine_*` 测试构造保留为 Tiantoken fallback,生产新环境变量优先。
- 验证:Tiantoken `/v1/models` 返回 HTTP 200126 个模型,含 `gpt-image-2``gpt-5.4-mini`);api-server Tiantoken 配置单测、platform-audio 全量测试、图片定向测试、前端 `apiClient` 定向测试、`npm run typecheck``npm run check:api-server-env`、编码 / fmt / diff 检查通过。未对音频上游提交生成任务,模型列表未列出 audio / Vidu 条目。
## 2026-09-15 删除旧版 Vidu 音效实现
- 决策:旧版 Vidu `audio1.0` 的 submit / poll / download builder、旧视觉小说与创建音效死代码、对应 platform-audio 请求类型和测试全部删除。历史素材的 `audio1.0` 展示与定价兼容数据保留;新编辑器音效仍只走 ElevenLabs,Suno 音乐链路不变。
- 验证:platform-audio 全量测试 55 条通过,api-server `cargo check` 通过,fmt / 编码 / diff 检查通过;仓库现役源码不再包含 `VIDU_AUDIO_MODEL``AudioTaskKind::SoundEffect` 或 Vidu submit/poll 实现。
## 2026-09-15 AGC 统一错误事件与项目诊断落库
- 背景:DirectProject 的 app-server 超时、MCP 参数错误、浏览器完成门误判和普通 Agent Runtime 失败分别投影为短文案;失败正文没有稳定落库,下一轮模型看不到上一轮失败证据,用户追问原因时可能继续试玩或重复修改。
- 决策:新增 `agent/runtime_error.rs` 作为统一错误事件与有界诊断 sidecar 边界。DirectProject 失败、Agent Runtime terminal failure 均持久化 `.agent/runtime/errors/<eventId>.json`,并将脱敏 assistant 终态写回 `project.jsonl`;前端只通过 `read_agent_runtime_error_detail` 读取脱敏详情。旧 `failure.json` 保留兼容,不把原始 stderr、凭据、URL/query、宿主绝对路径写入用户文本。
- 决策:错误使用稳定 `source / stage / code / retryable / publicText / recoveryHint / detailRef` 字段;试玩 attempt 越界返回终态错误并停止继续等待。素材完成门扫描实际 npm 源码模块,并把 manifest 中合法的自定义 art-spritesheet 路径纳入候选,构建和浏览器观察仍需通过既有完成门。
- 关联规范:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md` 的“2026-09-15 AGC 统一错误事件、诊断落库与验收反馈”;开发期计划见 `docs/project-memory/plans/【里程碑】AGC统一错误诊断与验收反馈-2026-09-15.md` 与对应实施计划。
## 2026-09-15 Direct 回合跨页面继续运行与活动项目面板
- 决策:采用后台继续运行语义。Direct 回合由进程内项目身份锁持有,页面离开不取消;重进项目通过活动回合只读快照与 Thread Manager bootstrap/consume 恢复忙碌态和进度。左上角面板复用同一快照列出正在运行的 Direct 项目并支持进入。
- 边界:快照不写项目文件、不进入公共 API、不跨应用重启恢复;读取失败保留上一份结果并单独提示,不改写成权限或审批失败。身份锁排他性、付费身份和项目写锁不变。
@@ -72,6 +72,10 @@ SpacetimeDB 任务统一先读取 `.codex/skills/genarrative-spacetimedb/SKILL.m
3. 确认相关当前文档与共享记忆已同步,且 docs 入口没有指向已删除或退役实现依据。
4. 提交标题使用中文,标题后逐行写明本次变更。
## Jenkins 定时版本调度
定时与版本比较只保留在 `Genarrative-Scheduled-Revision-Trigger` 一处:每小时用 `git ls-remote` 解析 `SOURCE_BRANCH` 远端 HEAD,与上一次触发过的 revision 比较,变化时才把同一个 `COMMIT_HASH` 同时传给 `Genarrative-Full-Build-And-Deploy``Genarrative-Agc-Windows-Build`,保证两条管线构建同一个版本。`Genarrative-Full-Build-And-Deploy``Genarrative-Agc-Windows-Build` 不得自带 `triggers` / `cron`,也不得在管线内再做一套版本去重;`npm run check:production-ops` 会拦住这两类回退。调度状态与生效步骤见 `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
## Gitea CI 依赖闭合
`.gitea/workflows/project-ci.yml` 的客户端门禁拆成八个 job,每个 job 只预热自己会构建的那几份依赖:`AI game creator shell Rust shard 1/4``4/4` 各只预取 AGC 壳 manifest 并各跑一片(AGC 壳那份 `Cargo.lock` 的 path 依赖已含 `platform-llm``platform-agent``agent-runtime-core``shared-contracts`),`AI game creator shell Rust smoke` 同样只预取 AGC 壳 manifest`agent-run` smoke 会用 `src-tauri/Cargo.toml` spawn `cargo run`),`AI game creator shell Rust crates` 预取 `server-rs/Cargo.toml` 与两个独立 crate`Native shell tests` 预取桌面壳与 AGC 壳 manifest`AI game creator shell web tests` 不触碰 Cargo,不预热。AGC 壳的 4 个分片 job、smoke job 与 crates job 只用 cargo 与 node 内建模块,因此不执行 `npm ci`。两个被 `server-rs/Cargo.toml` 排除、且没有提交 `Cargo.lock` 的独立 crate`agent-runtime-core``agent-runtime-orchestration`)只能在 `AI game creator shell Rust crates` 里用不带锁标志的 fetch。AGC 壳的 bin target 单测(约 2466 条)由 `apps/ai-game-creator-shell/scripts/run-rust-shell-test-shards.mjs` 编译一次后按 `--list` 名单分 4 片:CI 的每个分片 job 用 `--shard-index=<i>` 只跑自己那片,片内保持 `--test-threads=1` 并各自使用独立 `TMPDIR`,片与片之间靠 job 级并发摊开;本地不传 `--shard-index` 时仍是同一条命令把 4 片放进程里并行。不要改回「一个 job 内多进程并行这几片」——同一容器里它们会争抢共享 `HOME`、target 目录与固定临时路径,实测比整套串行还慢。每个分片 job 都会自校验「片并集等于全集且互斥」,因此改分片规则不会静默漏跑。Backend host workspace tests 使用 `cargo test --locked --workspace --exclude spacetime-module --no-fail-fast`,避免 `spacetime-module``spacetime-types` feature 统一污染普通领域 crate 的 host 测试;随后单独执行 `cargo test --locked -p spacetime-module --no-fail-fast`,由 `spacetime-module/src/active.rs` 在 host 测试构建期间提供仅测试期的 SpacetimeDB ABI 链接支持,使该 crate 的纯单元测试也纳入 Backend 门禁。`spacetime-module` 的 reducer / procedure 运行时行为仍必须通过真实 SpacetimeDB runtime/integration harness 验证,host 链接支持不得被当作运行时替身。Backend 另外执行 `cargo check --locked -p spacetime-module` 验证模块源码。AGC 壳检查还会运行 `platform-llm``shared-contracts` 的 server-rs workspace 测试,这些命令以及 AGC 壳测试必须带 `--locked`,避免在测试阶段重新解析 registry index;锁文件发生变化时应先更新受信任 CI 镜像缓存,再重跑门禁。
+65 -2
View File
@@ -1,5 +1,37 @@
# 踩坑与排障记录
## Windows 已登记生图资产未刷新
Direct 工具桥会 canonicalize 项目根,事件中的路径可能带 `\\?\` / `\\?\UNC\`,而前端项目路径仍是普通盘符或 UNC。失效监听不能直接比较原始字符串;识别为同一项目后,用当前项目路径重读 manifest,保留项目切换与 revision 门禁。普通 `agc_generate_image` 成功提交也必须发出失效通知,不能依赖整轮 Agent 结束。回归需覆盖两种 Windows 前缀、其它项目事件拒收,以及 Agent 尚未结束和后续失败时已登记图片卡片仍可见。
## 2026-09-14 严格 IPC 桩缺登记新命令时,症状可能是「unhandled rejection + 不相干的提示断言」,而不是同一处报错
- **现象**`ProjectDevelopmentView` 新增「项目打开时读生成任务账本」(`list_local_project_asset_generations`)后,两个**别的关注点**的用例同时红:`resourceCanvasManualLayout.test.tsx``AssertionError: expected [ Array(1) ] to deeply equal []`(严格桩把新命令记进 `unexpectedCommands`),并伴随 7 条 `Unhandled Rejection: TypeError: Cannot read properties of undefined (reading 'map')``appSurface/project-development.suite.ts` 的「布局读时提示」用例则因为新命令被当成 unexpected invoke 抛错、触发了新的提示条,导致 `queryBySelector('.game-resource-live-notice')` 断言失败。
- **原因**:这些用例的 `invoke` 桩是**严格白名单**(未登记即抛错或返回 `undefined`)。新命令在挂载期就被调用,于是:① 桩把未登记命令记进 `unexpectedCommands`/抛错;② 生产代码若对返回值无形状防御,就在 `undefined``.map` 产生 unhandled rejection。**两条失败都指不到真正的新增调用点**,很容易被误判成各自关注点的回归。
- **处理(现行口径)**:① 渲染 `ProjectDevelopmentView` 的桩统一登记 `list_local_project_asset_generations`(返回**数组**,空账本 `[]`Rust 侧返回 `Vec<AssetGenerationTaskRecord>`,不是 `{ tasks: [] }`);② `unexpectedCommands` 这类门禁**不要放宽**,只登记合法命令;③ 生产代码对 IPC 返回值做形状防御(`Array.isArray` 归一化),IPC 拒绝走既有提示路径,不产生 unhandled rejection`resourceCanvasAssetGenerationTaskModel.ts` / `resourceCanvasAssetGenerationQueue.ts` / `index.tsx` 的恢复 effect)。
- **易错点**:① 桩返回**非数组**时用例可能"看着绿"但同时报 unhandled rejection(实测:把 `[]` 误写成 `{ tasks: [] }` 就是 8 passed + 7 unhandled error),所以判"绿"必须同时看 unhandled 计数;② 新增挂载期 IPC 后要一次性 grep 所有 `ProjectDevelopmentView` 的桩,而不是等 CI 逐个炸;③ 提示条类断言(如「无读时提示」)会把「桩抛错」翻译成「多了一条提示」,排查时先看 unhandled,再看断言。
- **关联**`apps/ai-game-creator-shell/tests/resourceCanvasManualLayout.test.tsx``apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts``apps/ai-game-creator-shell/src/features/resource-canvas/resourceCanvasAssetGenerationTaskModel.ts`
## 2026-09-14 UI 编辑器返回后资源画布滚轮平移失效
- **现象**:资源管理打开 UI 编辑器再返回后,资源画布滚轮平移/缩放不再响应;返回前同一手势正常。
- **原因**:资源画布的非 passive `wheel` 监听绑定在 `resourceBookManagerRef` 当前 DOM 上,但 effect 只依赖 `handleResourceBookWheel``mode`。UI 编辑器切换会卸载旧 manager 并挂载新 manager,依赖不变导致新节点没有重新绑定监听。
- **处理**:将 `uiEditorRoute` 纳入 wheel effect 依赖,使进入/退出 UI 编辑器时先清理旧节点监听,再给返回后的新 manager 绑定同一处理器。
- **验证**`npx vitest run apps/ai-game-creator-shell/tests/appSurface.test.ts -t "restores resource canvas panning"`;回归用例覆盖打开栏目、wheel 平移、进入 UI 编辑器、返回并再次 wheel 平移。
- **关联**`apps/ai-game-creator-shell/src/view/project-development/index.tsx``apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts`
## 2026-09-14 AGC 就绪等待被 WMI 拖成分钟级:端口归属探测从 Get-NetTCPConnection 换成 netstat
- **现象**`npm run agc``[ai-game-creator-shell] starting backend stack``backend ready` 要等约 80 秒,中途反复出现 `等待配套后端就绪时归属校验未通过(api-server-owner-mismatch: 未知进程)`;而这段时间后端其实已经好了(实测 api-server 12:39:01 已在 8084 监听、`/healthz` 已 20012:40:19 才判 ready)。
- **根因(本机实测,不是推断)**:端口归属探测原实现用 `Get-NetTCPConnection -State Listen -LocalPort` 逐端口取 owner,而它底层走 WMI:**单端口单次 11.2 秒**;再叠加每个 PID 的 `Get-CimInstance Win32_Process`(热调用 3.3 秒、首次 18 秒)。三个端口一轮 ≈ 43 秒,而就绪等待每约 1 秒轮询一次 ⇒ 首轮几乎必然判负、要等好几轮才通过。同机对照:`netstat -ano -p tcp` 29 毫秒、`[System.Diagnostics.Process].MainModule.FileName` 4 毫秒、`Get-Process -Id` 19 毫秒;`Get-WmiObject` 4.2 秒、`wmic` 本机已被移除。结论是慢在 WMI 本身,换 cmdlet 没用。
- **处理**:探测脚本改为 ①`netstat -ano -p tcp` 取「端口 → PID」——监听行判据用**外部地址 `0.0.0.0:0` / `[::]:0`**(不依赖会被本地化的 State 文本),PID 取**最后一列**而不是硬编码下标(状态列本地化或被合并时也取不错,这个下标一旦写错会被 `$ErrorActionPreference = "SilentlyContinue"` 静默吞掉,表现为「探测永远返回空」);②`[System.Diagnostics.Process]::GetProcessById(...)` 读进程名与可执行文件路径;③只有核对 SpacetimeDB `--data-dir` 归属(或路径读不到要兜底标签)时才按 PID 取命令行,并按 PID 记 5 分钟 TTL 缓存、随探测请求经 `GENARRATIVE_KNOWN_COMMAND_LINES` 下发,让轮询只在首个周期付一次 WMI 成本。探测本身失败仍返回 null 走旧的退化分支,「归属无法证明就不复用」的语义不变。
- **验证**`apps/ai-game-creator-shell/tests/start-dev-stack.test.ts` 新增两条——「探测脚本使用 netstat 且不再出现 Get-NetTCPConnection」「命令行按 PID 缓存后随请求下发、TTL 过期即失效」;定向 vitest 55 passed。本机实测:不含 SpacetimeDB 端口的探测 368 ms(原约 22 秒)、含 SpacetimeDB 端口 3.8 秒、命中缓存 368 ms;`npm run agc:serve``starting backend stack``backend ready` 由约 80 秒降到 16.7 秒(其中归属校验只占 4.4 秒,其余是 SpacetimeDB + api-server 的真实启动时间)。
- **残留**:这台机器上首次 WMI 调用本身仍是秒级(曾见 18 秒),所以「新 SpacetimeDB PID 的第一次探测」仍可能多花几秒;命令行在进程存活期内不变,TTL 只用来限制 PID 复用造成的误判窗口。
- **关联**`apps/ai-game-creator-shell/scripts/start-dev-stack.mjs``readWindowsPortOwnerIdentities`)、`apps/ai-game-creator-shell/tests/start-dev-stack.test.ts``apps/ai-game-creator-shell/scripts/dev-windows-process.mjs`(退出清理仍走整份 `Win32_Process` 快照,自带 1 秒缓存,不在本次范围)。
## 2026-09-15 AGC JSON API 的响应体也必须有等待上限
- `fetchClientHttp` 的超时只覆盖请求到响应头返回;随后直接等待 `response.text()` 仍可能无限挂起。模型目录共用一个在途 Promise,响应体卡住会使后续刷新复用同一挂起请求、选择器持续忙碌。
- 成功 JSON 与错误响应体均复用 `readClientHttpResponseText` 的 15 秒上限;超时后保留最后一次有效目录并释放在途请求,手动重试重新发起请求。迟到的响应不得覆盖重试获得的新目录。
- 排查时区分接口未挂载(404)、未授权(401)、网络或响应体超时以及刷新无变化但缺少反馈;不能仅凭客户端启动 IPC 回退警告判断刷新失败原因。
## 2026-09-14 未知 JS 异常必须继续进入 error report
- **原则**:任何 JS 边界只要无法确认异常属于已知、已解决且有契约的业务失败,就必须保留原始异常并继续抛出,由全局 error report 链路采集;范围不限于 UI 编辑器、生成路径、剪贴板,也包括文件系统、权限、网络、插件和其它宿主调用。用户界面的 fallback(例如显示“复制失败,请手动复制”)只是附加的可继续操作提示,不代表异常已经被处理。
@@ -24,6 +56,14 @@
- **处理**`runNativeShellGate` 收窄为 `(group, gate)`,label 回到各门禁执行体里以字面量打印;分组能力与 `--groups=` 语义不变。脚本内已注明"不要把 label 抽成变量",外部壳的 `check-config` 是它的消费者。
- **验证**`node apps/mobile-shell/scripts/check-config.mjs` exit 0`node apps/desktop-shell/scripts/check-config.mjs` 已越过第 740–777 行的根脚本断言段(本机随后卡在本机不存在的 Tauri 生成产物目录,与本次改动无关);`--groups=contract`、vitest `scripts/project-ci-workflow.test.ts`、eslint 均通过。修复后 run 2097 六个 job 全绿。
- **关联**`scripts/check-native-shells.mjs``runNativeShellGate`)、`apps/desktop-shell/scripts/check-config.mjs``apps/mobile-shell/scripts/check-config.mjs`
## 2026-09-14 AGC 资源画布不要恢复「无条件重派生」,否则新增一张素材就整张重排
- **现象**:用户生成一张新素材后,画布上既有卡片全部移位,刚摆好的位置失效。
- **原因**`useProjectResourceCanvasLayout``rederiveAutomaticPositions=true` 会让**每一次资源协调签名变化**(新增 / 删除素材、改标签、改分类、拓扑签名变化)都丢掉全部 `manuallyPlaced=false` 坐标整体重算。签名里必然包含新素材,所以「新增一张素材」就等于「整张画布重排」;两个 hook 当时都开着它(type 侧无条件 `true`dependency 侧长期等于 `resourceGraphReady`)。
- **处理(现行口径)**:默认一律 `preserve`(只补新卡)。整张重排只由「整理画布」按钮调用 hook 的 `rederiveNow()` 发起,或由「关系图首次就绪」那一次按项目作用域的一次性 flag 发起。**不要**把 `rederiveAutomaticPositions` 改回长期 `true` / `resourceGraphReady`,也不要为「拓扑变了要立刻重排」再加自动触发点——那正是本条要修掉的行为。
- **易错点**:① 一致性判据是 `reconcileResourceCanvasLayout` 输出里**每个分区按 `(y, x, resourceId)` 排序**后的数组与来源逐项比较,所以手工构造 sidecar 夹具时要按同序写,否则会被判成「变了」而多写一次,用例里会看到意料之外的写回;② 关系图首次就绪那一次重算要等该侧 sidecar `ready` 之后再发(关系图可能先就绪),否则这一次会被吃掉;③ 依赖侧的一次性 flag 按**项目作用域**记账,不要挂到 `resourceGraphReady` 这类会随排序 tab 反复翻转的值上,否则每次切回依赖视图都会重排一次;④ 新素材聚焦按 `manifest.assets` 的新增 id 判定,首次打开 / 切项目必须先登记基线,否则一进工作台就跳到最后的卡上;⑤ 不要为了让「整理画布」按钮"一定有反馈"而把 `changed` 门拿掉——重算结果与当前坐标一致时不写盘是既有合同,关系图 `producerMappingTruncated` 时强行写回等于把一份来自不完整关系图的自动布局持久化(`appSurface.test.ts``keeps trusted truncated-graph depths through the workbench without persisting a flat automatic layout` 会红)。
- **验证**`npx vitest run apps/ai-game-creator-shell/tests/resourceCanvasManualLayout.test.tsx`(8 条);把两个 hook 配置改回旧口径会红 3 条。
- **关联**`apps/ai-game-creator-shell/src/view/project-development/useProjectResourceCanvasLayout.ts``apps/ai-game-creator-shell/src/view/project-development/index.tsx`
## 2026-09-14 客户端 CI 拆分后,选组运行会跳过未选分组,且必须同步分支保护
@@ -3841,7 +3881,7 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
- 现象:在 Jenkins Job 页面给 `MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID` 配了默认值,下一次加载 Declarative Pipeline 后又变空或恢复旧描述;04:00 Full Job 还可能因默认选择 `pause-after-stdb` 且 approvers 为空而失败。
- 原因:这些 Job 使用 Pipeline script from SCM`parameters {}``triggers {}` 会作为 Job property 回写现场配置;只改 UI 不是持久修复。构建编排如果不显式关闭下游 `PUBLISH_AFTER_BUILD`,还会受下游默认值漂移影响。
- 处理:credential ID 和参数默认值写回三个 Jenkinsfile;仅供开发使用的 dev 定时 Full Job 默认 `STDB_API_ROLLOUT_MODE=normal`,三路 Build 调用显式传 `PUBLISH_AFTER_BUILD=false`,再由 Full Job 统一按 Stdb → API → Web 发布。Secret 原文只放 Jenkins Secret File,旧 Secret Text 保留给 Import / Export。
- 验证:推送后让 Full / Stdb Build 用不存在的源码分支在 checkout 阶段 fail-closed,让 Stdb Publish 用空构建版本在 Prepare 阶段 fail-closed,以安全刷新参数 schema;随后只读检查三个 live `config.xml` 的参数描述和默认值,确认 Full timer 仍为 `0 4 * * *`rollout 默认值为 `normal`,并确认刷新运行未进入 publish / deploy stage。
- 验证:推送后让 Full / Stdb Build 用不存在的源码分支在 checkout 阶段 fail-closed,让 Stdb Publish 用空构建版本在 Prepare 阶段 fail-closed,以安全刷新参数 schema;随后只读检查三个 live `config.xml` 的参数描述和默认值,确认 rollout 默认值为 `normal`、Full 与 AGC Job 都不再带 cron(定时只来自 `Genarrative-Scheduled-Revision-Trigger`,并确认刷新运行未进入 publish / deploy stage。
- 关联:`jenkins/Jenkinsfile.production-full-build-and-deploy``jenkins/Jenkinsfile.production-stdb-module-build``jenkins/Jenkinsfile.production-stdb-module-publish``scripts/check-production-ops-guardrails.mjs`
## 维护模式内网全站放行不能信任 X-Forwarded-For
@@ -4953,7 +4993,7 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
- 原因:把“请求已入队”、“某个稳定 ID 已存在”或“job 已 completed”误当成整批业务记录已原子提交的证据。request fingerprint 只证明用户请求,不绑定最终 slot、派生记录、画布候选和 compact result;仅比较资源 ID 也无法发现内容漂移。
- 处理:用 `editor_generation_operation` 记录 durable receipt,分开 request fingerprint 与整笔 commit SHA-256。首次调用在同一 SpacetimeDB 事务中校验 lease 并写 object/resource/asset/binding/canvas/job/receipt;重放先查 receipt,再读回逐 slot 权威事实精确比较。receipt 缺失但 resource/asset/binding 已存在时失败关闭,不得补写 receipt;事务前已确认的 asset object 只能在 ID、bucket/key、owner、策略、媒体、来源和实体字段全部相等时复用。
- 时间与并发:`completed_at_micros` 必须为正数,object/resource/asset/binding/canvas 候选原时间字段与它一起纳入 commit SHA-256,不能在每次重放时重新取时;job 终态和完成事件只用 SpacetimeDB `ctx.timestamp`。canvas CAS 冲突后只刷新 project 并重算布局,不重跑 Provider / OSS。OSS 尚未进入该事务,无引用 object 仍是需另行清理的边界,不要宣称跨 OSS exactly-once。
- queue completion 不能把 inline 完整响应无条件同时复制到 `result``editor-agent-tool-call-result`。图集/UI 最多 64 个切片会重复携带 resource/asset/prompt/generationInputs,容易超过 job payload 512 KiB 上限并让整个原子提交回滚。必须先按普通 UI、Editor Agent、External API 的消费方契约裁剪,再把最终 JSON 交给统一 procedure。
- queue completion 不能把 inline 完整响应无条件同时复制到 `result``editor-agent-tool-call-result`。图集/UI 最多 256 个切片会重复携带 resource/asset/prompt/generationInputs,容易超过 job payload 512 KiB 上限并让整个原子提交回滚。必须先按普通 UI、Editor Agent、External API 的消费方契约裁剪,再把最终 JSON 交给统一 procedure。
- 消费方身份不能在提交前重新读取 summary 兼容快照来判断:该快照按设计清空 dedupe key 并删除 generationInputsEditor Agent / External API 会因此被误判成普通 UI。应在 worker 持有完整 claimed job 时把安全的 consumer kind 与 source identity 固化到调用上下文。
- procedure future 超时或连接断开不能直接映射为业务失败,远端事务可能已经提交。必须有界重放同一 prepared commit;明确 CAS 后才刷新 layout,且刷新 layout 应使用新时间,不能把项目 `updated_at` 回拨。receipt 不复制 queue payload,只存摘要并从 job 权威行回读;跨记录 object/project 一致性必须在事务内验证,不能依赖当前 builder 通常会携带完整 candidate。
- job 的 owner/kind/fingerprint/lease 都正确仍不够:`source_entity_id` 还必须绑定结果项目,来源资源必须另查存在性与 owner/project 归属;否则同 owner 的 job 可以误写别的项目,或伪造跨用户/跨项目血缘。
@@ -5562,3 +5602,26 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
- 原因:健康检查只能证明“有服务响应”,不能证明服务属于当前工作树;旧 `.app/dev-stack.json` 可能没有当前 `repoRoot``instanceId` 和服务级 dataDir 身份。
- 处理:先读取 `.app/dev-stack.json`,核对顶层 `repoRoot + instanceId`,再核对服务 `repoRoot + instanceId + dataDir + pid + port`AGC Vite marker 还必须带 `repoRoot + processId + port`。任何字段缺失或不匹配都拒绝静默复用,改为启动当前工作树自己的服务或明确提示清理。
- 验证:`scripts/dev.test.ts``apps/ai-game-creator-shell/tests/start-dev-stack.test.ts` 覆盖 snapshot identity 和旧状态拒绝复用;运行时记录实际端口、进程命令行和 dataDir,不要只记录 HTTP 200。
## 2026-09-15 登录失败提示必须保留接口返回原因
- **现象**:账号登录失败时页面只显示“登录失败”,用户无法判断是手机号、验证码、密码还是服务状态问题。
- **原因**:统一错误解析器只处理标准 `error.message/details` 结构;部分网关或旧兼容响应使用字符串 `error`,解析失败后回落到登录接口传入的通用文案。
- **处理**`parseApiErrorMessage` 同时支持字符串 `error`,标准嵌套结构保持原有优先级;未知或空响应继续使用通用兜底。
- **验证**`src/services/apiClient.test.ts` 新增字符串错误响应回归用例,定向测试 32 项通过,`npm run typecheck` 通过。
## 2026-09-15 Jenkins Stdb 发布临时目录必须允许服务用户遍历
- **现象**`Genarrative-Stdb-Module-Publish` 在备份和 SpacetimeDB 就绪后,于 `spacetime publish``Permission denied`
- **原因**Jenkins 以 root 运行时 `${HOME}/data/tmp` 位于 `/root` 下;即使发布临时子目录已 `chown``spacetimedb`,父目录仍不可遍历。
- **处理**:发布给 `--run-as-user` 的 WASM 临时目录改用 `/var/tmp`,继续使用随机目录并在退出时清理。
## Git hook 测试必须清除继承的 Git 仓库环境
- 在 hook 内运行临时仓库测试时,`cwd` 不会覆盖继承的 `GIT_DIR``GIT_WORK_TREE``GIT_INDEX_FILE`。未隔离的 Git/lint-staged 子进程可能向真实仓库提交 fixture,甚至把测试版 ESLint、Prettier 配置带入主分支。
- fixture 子进程统一清除 `GIT_*` 环境,并用一次性外层 linked worktree 验证引用、索引、配置不变;原有工程检查规则保持完整,不能用逐项关闭规则修复 fixture 污染。
- 2026-09-16 复核:从链接工作树 `git push`/`git commit` 时,Git 注入 `GIT_DIR=<主仓库>/.git/worktrees/<name>``GIT_WORK_TREE``GIT_INDEX_FILE`husky → npm → `check:repository-ci` → 夹具测试整链条继承。夹具 `git config user.name "Git Hooks Test"` 会写进共享 `.git/config`(此后所有提交 author 变成 `Git Hooks Test`);夹具 `git init` 按是否带 `GIT_WORK_TREE` 分别写成 `core.bare=true``fatal: this operation must be run in a work tree`)或 `core.worktree=<临时夹具目录>``git status` 实际在操作临时目录)。
- 处理:钩子与门禁入口先 `unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_COMMON_DIR GIT_PREFIX GIT_CONFIG_PARAMETERS GIT_CEILING_DIRECTORIES`;夹具 Git 调用在命令前自检 `rev-parse --show-toplevel` 等于夹具目录,落到外部仓库立即失败;守卫用例的子进程必须真的继承 `GIT_DIR`,否则断言会空转。
- 验证:`git config --show-origin --get user.name` 出现 `file:.git/config Git Hooks Test``git rev-parse --show-toplevel` 指向 `%TEMP%\genarrative-pre-push-*\repo` 都是被污染的确定性证据;被 `core.worktree` 劫持期间执行的 `git pull` 会把检出写进临时目录,真实工作树整体落后(本次 93 个文件),配置修好后用 `git checkout HEAD -- .` 回填。
- Vitest 的 `toHaveBeenCalledWith` 匹配任意一次调用,失败输出会列出其它命令;应先定位相同命令的真实参数差异,不能由其它调用的序号推断时序故障。
- 存在后台轮询的 IPC mock 不应要求目标命令占据全局最后一次调用。验证刷新时先记录调用边界,再筛选该边界之后的目标命令,严格核对其最后一次参数,避免后台查询影响断言,也避免旧调用掩盖刷新未执行。
@@ -51,6 +51,11 @@ SpacetimeDB crate、SDK、CLI / standalone 与生成 bindings 按 `2.8.3` 对齐
## AGC DirectProject 与 UI workflow
- DirectProject 对话先在完整历史中按回合/原始 item 身份关联,再分页渲染;每个回合只有一个呈现入口。有流按 item `seq` 交替文本和工具,无流采用历史正文;禁止位置猜配或同时展示累计回复与 item 正文。流写入单调归并,收尾等待落盘任务,不按磁盘“最后一段”猜最终回复位置。详见 AGC 实施计划的“DirectProject 回合展示唯一归属”。
- 回合生命周期只由活动 client 回合快照和 Direct 事件恢复;Provider 的历史终态通知不能创建活动 client 回合。消息发送时间保存在历史信封,原始 item 不混入宿主字段;完成后的中间文本和工具默认收进“执行过程”,最终回复及失败提示保持可见。
- AGC 安装产品名统一为“陶泥儿”,由 Tauri `productName` 控制安装项、快捷方式与 EXE 产品描述;Windows 内置 Codex 安装到顶层 `coding-agent/win-x64/`,打包资源映射与运行时查找路径必须一致。内部可执行文件名与应用 identifier 保持稳定。
- 新 Web 游戏为 `game/` 下的 npm + Vite + Phaser 4.2.1 工程,使用包导入且允许其它依赖;npm 预览与导出只读取 dist,运行素材需纳入构建。单 HTML → Phaser 迁移固定走 DirectProject:文件落盘后先用受控 `project.bootstrap``game` 执行无参数 `npm install`,再用支持相对 cwd 的 `project.verify` 构建并确认 `game/dist/index.html`,已有单 HTML/Godot 不通过 JSON Generator 伪装成 npm 工程。
- 通用 Agent Rust 分层为 `agent-runtime-core`catalog、执行生命周期、ToolHost/spawn/all-join/Provider 契约)、`agent-runtime-orchestration`(动态无环任务图、ready、依赖波次、返工下游闭包和受限自主扩图提案)与 `platform-agent` 游戏适配器;循环返工通过新 pass / epoch 表达,不在单张依赖图中建立回边。LLM 可经宿主结构化 function call 提出新增节点/边,编排层只生成经校验的新候选图,epoch 与持久化仍由宿主掌控。
@@ -18,6 +18,7 @@
- 修改范围保持聚焦;优先扩展现有系统、页面、组件、DTO 和脚本,不新建平行入口或业务真相。
- UI 开发优先复用现有公共组件;跨页面或跨端重复的视觉/交互模式应沉淀到 `packages/shared`,由现有页面迁移使用,禁止在业务页复制同类 UI。共享组件只承载通用表现与交互,不下沉领域规则、后端副作用或正式业务状态。
- AGC 当前 Agent 与策划 Agent 的消息层级共用 `packages/shared``AgentMessageContent`:正文使用 `body`,思考、中间输出与工具调用使用 `process`;宿主不按 Agent 类型重新定义过程字号和颜色,错误状态保留语义色。
- 后端遵循 `module-*``spacetime-module``spacetime-client``api-server``platform-*``shared-contracts` 的现役边界。
- 前端只负责表现、交互和临时 UI 状态;正式状态来自后端投影、API 或持久化契约。
- 对已明确退役且无现役调用方、公开契约、持久化迁移或活跃实例的对象,不写兼容实现、维持旧行为的测试、墓碑注释或墓碑文档。
Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

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