合并 master:resume 入口与 ProjectDevelopmentView 各留双方新增
Project CI / Frontend tests (pull_request) Successful in 4m27s
Project CI / Backend tests (pull_request) Successful in 5m31s
Project CI / Repository checks (pull_request) Failing after 11m41s
Project CI / Native shell tests (pull_request) Successful in 15m46s

两处冲突都是双方各自新增、互不相干:

- recovery_scan.rs:master 在 resume 入口加了
  recover_direct_taonier_regeneration_workflow_at(硬 `?`),分支加了策划
  GDD 审批投影恢复(分三路容错、不硬传播)。都留;master 那句放前面,
  保持它原有语义不受分支块影响。
- WorkspaceLauncher.tsx:master 的 walletEntry 与分支的 planningStartMode
  是同一个 JSX 元素上两个独立 prop,都留。

typecheck 干净,eslint rc=0,cargo check --all-targets 通过,
appSurface 421/421,分支自有过滤器(plan_envelope_repair_tests 4、
native_agent_delegate 9、prompt::tests 32)全绿。clarification 仍是本机
既有的 1 条锁超时基线失败。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-24 03:56:17 +00:00
80 changed files with 26867 additions and 2004 deletions
@@ -418,6 +418,11 @@
"ExternalApiKey": []
}
],
"parameters": [
{
"$ref": "#/components/parameters/IdempotentCreateKey"
}
],
"requestBody": {
"required": false,
"content": {
@@ -439,11 +444,17 @@
}
}
},
"400": {
"$ref": "#/components/responses/BadRequest"
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"409": {
"$ref": "#/components/responses/IdempotentCreateConflict"
}
}
}
@@ -684,6 +695,9 @@
"parameters": [
{
"$ref": "#/components/parameters/ProjectId"
},
{
"$ref": "#/components/parameters/IdempotentCreateKey"
}
],
"requestBody": {
@@ -718,6 +732,9 @@
},
"404": {
"$ref": "#/components/responses/NotFound"
},
"409": {
"$ref": "#/components/responses/IdempotentCreateConflict"
}
}
}
@@ -766,6 +783,11 @@
"ExternalApiKey": []
}
],
"parameters": [
{
"$ref": "#/components/parameters/IdempotentCreateKey"
}
],
"requestBody": {
"required": true,
"content": {
@@ -795,6 +817,9 @@
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"409": {
"$ref": "#/components/responses/IdempotentCreateConflict"
}
}
}
@@ -1557,6 +1582,18 @@
"maxLength": 128,
"pattern": "^[!-~]+$"
}
},
"IdempotentCreateKey": {
"name": "Idempotency-Key",
"in": "header",
"required": false,
"description": "可选的稳定创建幂等键。同一账号、接口与键使用相同规范化请求体重试时返回首次结果的当前记录,即使该记录后来已被修改;同键不同请求体返回 409。首次结果已删除时,同键重放同样返回 409 且不会重新创建。网络结果不确定时必须复用原值。",
"schema": {
"type": "string",
"minLength": 1,
"maxLength": 128,
"pattern": "^[!-~]+$"
}
}
},
"responses": {
@@ -1610,6 +1647,16 @@
}
}
},
"IdempotentCreateConflict": {
"description": "稳定创建幂等冲突:同一 Idempotency-Key 携带不同规范化请求体,或首次创建结果已删除 / 缺少耐久回执;服务端不会重新创建或重复首次副作用",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"UpstreamError": {
"description": "上游生成服务失败",
"content": {
@@ -66,7 +66,10 @@
- 原生读取必须在命令入口、权限 / 登记复核后、打开文件后、每个固定上限读取块之间、签名 / 图片结构校验前、漂移复核前和 base64 编码前检查取消。取消的排队任务不得打开文件,取消的在途任务不得生成 data URL 或 Blob URL;全局 permit 只能在对应原生任务结束后释放。重复 `requestId` 失败关闭,取消未知或已完成 scope 幂等成功;成功、失败和取消均必须清理活动 request / scope registry。为防止近期 request ID 重放和已取消 scope 复活,原生管理器继续保留有界 tombstone:seen request 最多 `8192` 项;非活动 cancelled scope 的保留预算为 `1024` 项,仍有请求的已取消 scope 必须临时钉住,最后一个请求结束后立即重新淘汰到预算内。明确取消只静默收口旧 scope,当前 scope 的真实 transient / permanent 失败语义不变。
- 安全读取失败、图片解码失败或视频无可解码画面时,卡片展示稳定类型占位,不挂载破图,不降级为项目外 URL 或裸路径读取。滚动可见性不得自动重试已失败预取;读取漂移、文件替换和通用暂时失败标记为 transient,用户再次打开详情或点击播放时可以显式重试。超尺寸、损坏、类型不支持和不安全 SVG 等永久失败继续缓存且不得用“关闭后重试”误导用户。
### 3.4 数值微调
### 3.4 运行视窗与数值微调
- 运行视窗必须占满中央工作区为游戏保留的可用区域。loopback 预览页通过客户端本地 preview server 注入的只读尺寸桥上报文档实际宽高;宿主只接受当前 iframe、当前 loopback origin 的固定版本消息,并将完整游戏文档等比缩放、居中放入视窗。iframe 首次适配后发生的真实内容增高或缩短仍必须被接受;仅浏览上下文宽高回灌或内容宽高未变化时保持当前状态,不触发重复渲染。
- 窗口或中央区域尺寸变化后必须重新测量和适配;内容已经放得下时保持 `1:1`,不得无故放大。游戏文档宽高超过视窗时缩小整体画面,不显示 iframe 横向或纵向滚动条,也不得用单纯裁切替代完整展示。尺寸桥以根布局 `ResizeObserver` 为主,并在页面可见时每 `500ms` 至多探测 `512` 个元素作为绝对定位溢出的低频兜底;探测截断时不得用部分样本下调尺寸,viewport 耦合的 `100vh / 100% / bottom / right` 布局也不得形成自反馈。相同测量结果去重,不监听整页属性、文本或子节点突变;桥不读取项目正文、不修改 manifest、游戏文件或运行业务状态。桥脚本只能注入到真实 HTML 标签上下文,不能把脚本、样式、模板或注释中的 `</body>` / `</html>` 文本误判为结束标签;省略结束标签的 UTF-8 HTML 仍需安全注入。
- 数值修改立即写入当前项目的编辑态配置。
- 当前已拉起的体验预览和测试切片不热更新;必须重新拉起后才能消费新值。
@@ -108,6 +111,9 @@
- 第一批共享 chrome 固定覆盖画布动作按钮、工具栏、工具分组和分隔符。按钮的默认、悬停、键盘焦点、选中、禁用和主次色语义由共享层表达;宿主只提供图标、文案、事件与业务禁用条件。
- 主站账号、钱包、服务端项目、云端素材库和生成面板仍留在网站宿主;客户端本地项目、manifest、Runtime、审批、草稿、生成与正式提交仍留在 Tauri 宿主。共享视觉组件不得读取这些业务事实。
- 客户端 UI 对齐采用“同视觉、同组件、保留四区布局”,不复制主站整页布局。资源总览、运行视图和 Supervisor 对话继续是 Game Agent 工作台独有语义。
- 工作台四区作为同一应用工作面直接衔接,外层不使用卡片圆角或留白间距。中央主视窗与右侧 Supervisor 使用各自的平台区域底色,并以稳定的竖向分隔线明确边界;不通过外层圆角容器、阴影或区块间缝重新包装四区。
- 右侧正式钱包入口属于客户端宿主 chrome,在普通项目工作台与 UI Editor 子路由中都必须持续可见、键盘可达且可操作,不得因进入素材编辑壳或隐藏全局 chrome 而丢失余额、充值和使用详情入口。
- 资源总览的“资源依赖 / 资源类型”视图切换使用连通的分段按钮组,相邻选项共享边界并保持唯一选中语义。每个分段都必须有清晰的键盘焦点指示,焦点环不得被分段容器的圆角或 `overflow` 裁切。
- 右侧 Supervisor 对话中,用户消息使用右对齐、最大宽度受限的主题暖色气泡,assistant 消息保持左对齐;消息换行不得产生水平溢出,执行过程卡继续占满消息区可用宽度。气泡正文在 light / dark 平台主题下均须满足 WCAG AA 普通文本 `4.5:1` 对比度。
- 客户端正式产品仍只按最小 `1280×720` 横屏合同交付,并保留 `1280×800` 默认窗口与既有基线验收;更窄浏览器样式只负责不崩溃和开发兼容,不改成移动端创作工作台。
- 普通用户界面不默认展示内部错误码或本机绝对路径,也不展示 External Editor Base URL 或 Developer API Key;官方服务地址由客户端构建固定,远端编辑自动使用当前陶泥儿登录态。仅独立 standalone game-chat release 或显式高级自定义模式在隔离配置页维护 External v1 URL/Key,确需诊断的信息进入受控详情或开发模式。
@@ -567,6 +573,9 @@ type ProjectAgentMudPointAttribution = {
4. 主站现有图片编辑器的按钮名称、tool selection、hover/focus/pressed/disabled 行为与工具栏位置不因共享抽取回归;网站账号、钱包、素材库、生成和导出逻辑不下沉共享层。
5. 客户端生成、保存、取消、恢复、登录失效与余额不足只更换视觉承载,不改变 Host Port 调用、幂等身份、草稿 CAS、manifest/revision、Runtime 审批或钱包刷新语义。
6. 阶段一、二冻结合同并让主站消费共享 chrome;2026-08-06 的阶段三至五已经完成客户端中央区、Supervisor 和 Agent Dock 的正式换肤、定向测试与 `1280×800` 验收。独立 Vite 没有登录会话时继续显示真实登录门禁,不为视觉测试增加绕过入口。
7. 普通工作台和 UI Editor 子路由均不显示工作面外层圆角或区块留白;中央区与右侧 Supervisor 的区域底色可辨识,竖向分隔连续、无错位。
8. 右侧正式钱包入口在普通工作台和 UI Editor 子路由都始终可见、键盘可达并能打开余额、充值与使用详情;路由切换不得使入口消失或失去交互。
9. “资源依赖 / 资源类型”以连通分段按钮呈现,点击与键盘操作均只保留一个选中项;使用 Tab 定位和键盘切换时焦点指示清晰、完整,不被容器边界或 `overflow` 裁切。
## 8. 非目标
@@ -7632,8 +7632,8 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 路由决策:普通 AGC 登录后直接调用固定官方 origin 下的网站现役 `/api/editor/*`、`/api/assets/*` 与 `/api/runtime/external-generation/jobs/*`,不再要求用户配置 External Editor Base URL 或 `tnr_sk_...`。普通 release 的官方 origin 继续服从 2026-08-10 的 dev API 固定规则,本地 dev 使用配套代理。`/api/external/v1` 的 Bearer Developer API Key 契约保持独立且不接受网站 Access Token,只供第三方 Agent/CLI、独立 standalone game-chat release 与显式高级自定义模式;服务端不为普通用户自动签发、代管或下发 Developer Key。
- UI 与 standalone 例外:普通 Launcher、工作台、已认证 debug game-chat 和运行时配置页不展示或维护 External Editor URL/Key。独立 standalone game-chat release 仍绕过 `AuthenticatedClient`,只能在用户显式配置其隔离 AppData 的高级 External v1 URL/Key 后使用云端画板能力;凭据缺失时明确不可用,不得回退到普通登录态、内置 Key 或隐式公共账号。
- 会话 CAS:WebView 登录、refresh、退出和换号通过窄 Tauri/Runner 协议同步 `userId + 短期 Access Token + authGeneration` 到 GUI/Runner 进程内存。generation 单调递增,只有不旧于当前值的安装/清除可以生效;每个业务请求在发出前冻结 `ownerUserId + authGeneration`,在响应解析、账本推进、媒体安装和正式项目提交前都重新核对。旧 generation 的迟到安装、响应或清除不得覆盖新账号,也不得把旧账号产物安装到新账号项目。
- 凭据边界:Access Token、refresh Cookie、Authorization 和 authGeneration 不进入 AppData、项目、manifest、生成账本、Agent prompt、conversation、Agent DB、trace、日志或普通错误;durable 账本只保存非敏感 ownerUserId。Runner IPC 只允许专用 session install/clear 消息携带 Token,普通生成/恢复命令参数继续不得携带 Access Token 或 Developer Key。
- 会话 CAS:GUI 取得 owner OS 锁时产生随机 `owner epoch`,并持久化只含 `owner epoch + session revision` 的私有 claim。WebView 登录、refresh、退出和换号先单调推进 durable session revision,再以 `runner.attach_gui_owner` 同步 `userId + 短期 Access Token + authGeneration`;Runner 只接受 epoch/revision 与 durable claim 完全一致的 attach。同一 epoch 内 generation 仍单调递增,只有不旧于当前值的安装/清除可以生效;新 GUI epoch 则可替换旧进程留下的更高 generation。每个业务请求在发出前冻结 `ownerUserId + authGeneration`,在响应解析、账本推进、媒体安装和正式项目提交前都重新核对。旧 epoch/revision/generation 的迟到 attach、响应或清除不得覆盖新账号,也不得把旧账号产物安装到新账号项目。
- 凭据边界:Access Token、refresh Cookie、Authorization 和 authGeneration 不进入 AppData、durable claim、项目、manifest、生成账本、Agent prompt、conversation、Agent DB、trace、日志或普通错误;durable claim 只保存非敏感 epoch/revision,durable 账本只保存非敏感 ownerUserId。Token 只允许随与 durable claim 完全匹配的 `runner.attach_gui_owner` 进入 Runner 内存;旧 `platform.session.install/clear` 不再是授权入口,普通生成/恢复命令参数继续不得携带 Access Token 或 Developer Key。claim 缺失、不可读或失配时 Runner 必须立即清空平台会话并阻断 Runtime 请求;同步失败必须隔离或停止旧 Runner,不得在 GUI 已切换后继续使用旧账号权威。
- 401/refresh:普通模式首次 `401` 进入当前 authGeneration 的全局单飞 refresh;其它请求等待同一 Promise,但各自 deadline、取消和账号归属独立。refresh 成功只在原 generation/owner 仍当前时以更高 generation CAS 发布新 Token,然后每个调用最多使用原 endpoint、原始请求字节和原 `Idempotency-Key` 重试一次。refresh 失败、账号已切换或重试仍为 `401` 时停止网络并保留原 operation;`403` 表示当前账号权限不足,不触发 refresh。高级 External v1 模式只允许用户修正自己的 Developer Key,并继续原 operation。
- Durable 身份与换号:普通账本身份固定为 `official-platform-v1 + 官方 origin + ownerUserId`,高级账本身份固定为 External service origin,二者均不绑定可轮换 Token/Key。退出或换号会提升 generation、中止并脱离旧请求;新账号对旧 owner 的 `prepared/accepted/running` 账本必须零 POST、零 GET、零下载、零安装、零归档,只有重新登录同一 owner 后才能恢复。`401/403/404`、网关错误或轮询超时不能在 owner 不匹配时证明旧 operation 的终态。
- 幂等与站内响应:`prepared` 只可精确重放账本冻结的原 endpoint、原始正文和原幂等键;`accepted/running` 只查询原 operationId。Access Token/Key 轮换、refresh、重启、退出或换号都不得创建替代 operation、修改正文或重新扣费。站内生成适配 `200 + queueState.operationId`、inline 完成和 `job` 状态包装;External v1 继续固定 `202 + operationId`,两者共享相同 owner、计费、稳定引用与本地事务门禁。
@@ -14805,3 +14805,42 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 素材生成确认、离开确认和旧服务身份确认沿用现有独立 modal,不在当前面板下方追加内容。modal 打开后焦点必须进入对话框,并同时隔离工具栏、画布视口、缩放/小地图、状态区和并存操作层,使背景从键盘焦点顺序与 accessibility tree 中退出。
- `Tab` / `Shift+Tab` 必须在当前 modal 内双向循环;非异步 pending 状态允许 `Escape` 安全关闭。关闭后优先恢复到原触发器,自动弹出的 modal 则回退到可操作的工具栏入口,不能把焦点遗留在已卸载节点或被隔离背景中。
## 2026-08-23 AGC 本地资源与 External Editor 账号绑定分离
- 权威边界:本地项目 ID、manifest asset、正式本地文件和内容摘要属于设备上的本地项目;`canvasProjectId / resourceId / assetObjectId / objectKey` 属于具体 External Editor 服务 principal。manifest 中现有远端字段继续保留生成来源,不再承担“当前账号可编辑句柄”,本轮不修改共享 manifest schema。
- 私有投影:统一在 `.agent/runtime/external-editor-bindings/` 持久化 active binding。项目 binding 按服务 origin、平台 userId 或 Developer Key 摘要以及本地 `projectId` 分区;资源 binding 再绑定远端项目、本地 asset ID、源 SHA-256、媒体类型和 canonical asset kind。sidecar 不保存 Token、Key、Cookie、Authorization、签名上传表单、媒体正文或绝对路径。
- binding 完整性与并发:lookup key 与稳定 payload 内容指纹分开校验,远端 project/folder/resource/object ID、object key 和尺寸的任一改写都必须失败关闭。同一 `本地项目 + principal` 的首次 binding 建立串行化覆盖复读、远端创建与安装,并发生成不得各自创建画布或留下孤儿项目。
- binding schema 迁移:当前 project/resource sidecar 显式为 v2 且 payload fingerprint 必填。旧 v1 文档仅能由独立 `deny_unknown_fields` wire 结构命中,在 key、principal、local project/source、remote 引用、object key、时间和尺寸等现役不变量通过后计算指纹并原子回写 v2;未知字段或身份 / source / 路径篡改均失败关闭且不回写。
- 切号语义:账号 B 打开账号 A 曾生成资源的同一本地项目时,不访问、迁移或覆盖 A 的私有画布;B 从本地正式文件在自己的远端项目重新上传、confirm、登记并形成独立 binding。切回 A 时复用 A binding。项目标题只用于远端展示,不能作为同名项目的权威关联;项目改名不换 binding。
- Runner 切号权威:Runner 协议 v7 规定 GUI owner 锁每次取得都创建新的随机 owner epoch,每次平台会话变更先推进 durable session revision claim。Runner 只能通过与该 claim 完全匹配的 `runner.attach_gui_owner` 替换会话,因此新 GUI 即使从较低 `authGeneration` 开始也能取代旧进程权威。claim 失配时立即清空 Runner 平台会话并阻断 Runtime,旧 `platform.session.install/clear` 只失败关闭而不再变更权威;GUI 与 Runner 同步失败时必须隔离或停止旧 Runner。
- renderer / native 会话提交:renderer auth generation 只负责 UI 转换和迟到读取失效,native install / clear 使用独立只增 generation 并在同一串行队列执行。登录、refresh、当前用户查询与 native commit 共用请求前冻结的 API origin,refresh singleflight 按 origin 分区;入队前冻结 `user + token + origin`。候选账号只在 native 成功且仍属当前 auth generation 时提交;期望权威显式表示为 account 或 `null`。迟到 native 完成必须以更高 generation 对账回当前期望权威,对账失败清空 renderer committed 会话与 Token。
- stale refresh 决策:queued commit 的 expected generation 过期时,early return 先恢复 renderer 当前 committed / desired Token。旧账号的 refresh 失败在当前 owner 已变更时只返回 `stale`,不发送会让 `AuthenticatedClient` 清空新账号的 failed 结果,也不执行新账号 native clear。
- 在途边界:资源编辑、素材画布生成和 Agent 美术生成的 `prepared / accepted / running` 账本继续锁定发起 principal。账号切换后只能停止请求并保留原 operation 供切回或对账,不得把在途副作用迁移到新账号,也不得重建正文或重复 POST。只有尚未提交远端生成的新操作可以为当前账号建立新的项目和资源 binding。
- 在途会话复验:提交返回 `operationId` 后先持久化 accepted 身份,再在每次 poll、download 和本地 commit 前校验冻结会话;同步本地 commit 必须持有冻结会话租约,使切号与安装线性化。Tauri 手工生成入口也必须使用稳定的 durable operation slot,不能退化成临时幂等键。切号后不得继续旧账号网络或把旧账号结果安装到本地项目。
- 账本 owner 线性化:资源编辑与素材画布的 owner 绑定、服务身份指纹 / 挑战和确认写入,均在同一冻结 platform session 租约下完成,且账本已有 owner 必须精确匹配 `userId + api origin`。Developer Key 账本保持 `owner=null`;请求 / 确认挑战的直接命令调用也不具备跨 owner 写入权限。
- 恢复投影:资源恢复列表持有当前 session 租约扫描,远端账本只对精确 owner `userId + api origin` 可见;未绑 owner 的 Developer / legacy 远端账本与 owner 不完整账本均失败关闭,纯本地编辑仍可见。renderer auth generation 变更同步作废恢复 read epoch,清空列表、挑战和动作状态,迟到读取不得恢复旧账号投影。
- Direct 审计对账:图集只读恢复已完成 file + manifest 登记后,`asset.register` 追加审计一旦成功或结果未知,后续登记 / binding 失败都保留 file、manifest 和已落盘 audit,统一进入 `reconciliation-required`。禁止为了伪造原子性而回滚 manifest、删文件、重试未知审计或重新生成。
- 账本迁移:资源 canonical 身份从历史 manifest `resourceId` 收口到本地 asset ID 时,旧账本只在 source asset、路径和 SHA-256 一致时可白名单恢复;新请求仍只信任本地 canonical 身份。
- 集成边界:`resource_editor`、素材画布参考准备、`canvas.asset_generate` 的 art-spec 派生和直连只读恢复统一消费当前 principal binding;PR 176 rebase 后必须删除或整合其局部 canonical cache,不能保留第二套账号身份系统。External v1 的项目、素材目录和项目资源创建接口新增可选 `Idempotency-Key` 请求头并同步 OpenAPI;客户端用 binding key 派生稳定值,服务端按 `owner + 接口命名空间 + key` 生成稳定 ID,同键同正文返回原记录、同键异正文冲突。
- 验收:至少覆盖 A 生成到本地后切 B 重登记、B 请求零 A 远端 ID、重启后复用 B、切回 A 复用 A、两个同名本地项目隔离、项目改名不漂移、源 SHA 或 kind 变化重登记、A 在途任务切 B 零网络,以及 sidecar 零凭据和身份篡改失败关闭。
## 2026-08-23 AI 游戏运行视窗按预览文档实际尺寸自适应
- 背景:项目开发工作台的中央运行视窗尺寸小于部分生成游戏的页面布局高度时,滚动条来自 loopback iframe 内部;宿主只隐藏 overflow 会直接裁掉标题、Canvas 或控制区,不能满足完整试玩。
- 决策:客户端本地 preview server 为 UTF-8 HTML 注入固定同源尺寸桥;注入器按真实 HTML tokenizer 边界保守处理注释异常结束、DOCTYPE 引号、script escaped / double-escaped、raw-text、template、plaintext、foreign content 与重复 `src`,省略结束标签时只在已证明安全的文档位置注入。桥通过根节点 `ResizeObserver`、页面 load、窗口 resize 与字体就绪重新测量;页面可见时以 `500ms` 低频兜底探测至多 `512` 个元素边界,探测截断时不采用可能低估的部分样本,并排除随 viewport 同步变化的布局自反馈。它不订阅整页 DOM 突变,并只在尺寸元组真实变化时上报文档与浏览上下文宽高。宿主只接受当前 iframe source 与当前授权 loopback origin 的固定版本消息,按实际内容和可用容器计算最大为 `1` 的等比缩放并居中显示;首次缩放后仍接受内容宽高真实变化,重复内容尺寸或仅 viewport 回灌不更新状态,容器 resize 后重新以原生视口测量。运行视窗不再提供 iframe 横纵滚动条,内容适配不改游戏文件、manifest、PreviewRegistry 或运行业务状态,非 UTF-8 HTML 保持原样。
- 验证:前端纯函数覆盖无需缩放、纵向超高缩放、首次缩放后的增高 / 缩短、重复内容尺寸去重、过期 viewport 与非法消息;Rust preview server 测试锁定尺寸去重、无全页 MutationObserver、低频有界探测、截断保护、固定 body 与 viewport 耦合布局不振荡、真实 HTML 上下文注入、注释异常结束、DOCTYPE 引号、script escaped / double-escaped、raw-text / template / plaintext / foreign content、省略结束标签、大小写结束标签、重复 `src` 和幂等注入;再以桌面最小窗口和更高窗口人工确认完整画面、动态内容变化后仍适配、无纵向滚动条且指针 / 键盘交互仍可用。
## 2026-08-23 Direct Codex 显式重生成与切片一等资源
- 决策:`taonier_prepare_game_art` 使用 `reuse-or-create | regenerate` 两态合同;旧调用缺省复用,只有用户显式重做或换风格才允许重生成。`regenerate` 只绕过本地完整包复用,不绕过未决 External Editor operation;旧账本 prompt 与本次 prompt 不一致时必须进入结果未知/对账,零新 POST。
- 重生成前置门:`regenerate` 只要求旧规范图和背景图可下载、可解码、来源一致且有可信登记,以便完整恢复两项旧字节和 manifest entry;历史主图集、私有回执、公开清单或 canonical 切片可以缺失。八个严格路径及受管顶层 asset identity 必须按真实状态逐项冻结为 `Present/Some` 或 `Missing/None`,不能把缺失状态伪造成空内容。只有规范图或背景图缺失/无效时才提示先用 `reuse-or-create` 修复基础素材。
- 整包事务:`regenerate` 在任何付费阶段前持久化绑定意图摘要与稳定 `clientTurnId` 的客户端私有 v4 workflow;状态固定为 `resetting / in-progress / compensating / completed`,专用 `direct-codex-art` 跨进程执行锁覆盖整个付费生命周期。每个已安装阶段立即持久化旧字节、旧 manifest entry 与新结果双 CAS 锚点,后续阶段失败时可跨进程重启继续补偿,但不删除已完成阶段账本。`completed` 必须持久化有界且脱敏的完整工具结果,同一 `clientTurnId` 的完成回包丢失只等值重放该结果、零新 POST。新的显式用户回合先以 `completed -> resetting` 持久化目标身份,再清理旧账本并转为 `in-progress`,不删 workflow。只有零阶段账本、零替换锚点的孤立 `in-progress` 空壳允许新回合原子接管;未知版本、旧 v2/v3 及其余冲突全部失败关闭,旧字段不得通过 serde 缺省静默升级。
- 恢复入口:通用恢复扫描与 Direct 回合启动前置恢复都必须发现 `resetting`、`compensating` 和带替换锚点的 `in-progress`,并在专用执行锁内清阶段、补偿和中性化。补偿恢复旧文件并清除本地 replacement CAS 锚点,但保留已 `prepared / accepted` 的阶段账本、原 `Idempotency-Key / operationId`;同冻结意图续跑必须复用原请求身份,未知账本在文件 mutation 前失败关闭。冻结意图一致时,新进程 invocation 可接管未完成阶段;`completed` 以原始外层 `clientTurnId` 等值回放,不受模型 brief 重采样影响。App 在 Direct 调用前幂等持久化原始 User 消息与稳定回合 ID,Tauri 在成功返回及 `completed` 事件前以同一回合 ID 幂等持久化 assistant 终态,项目重开只续跑最近一条真正未回答的合法原始回合。
- 资源投影:工具返回主包路径、已登记切片路径、安全 `resources` 身份,并分开保留普通 warning 与 slice warning。标准核心图集首次创建和重生成都必须严格提交恰好四张 canonical 切片;alpha、可见像素、规范像素唯一、Canvas resource/asset identity 唯一任一不满足即失败。旧项目补登记与已有完整登记都必须由客户端私有回执交叉验证,不能把可编辑公开清单或顶层 manifest 中的自述身份单独升级为权威源;部分登记要么按私有回执事务补全,要么明确 warning。规范图只作 reference,不再计为运行态平台素材。
- 隐私投影:成功结果中的普通 warning 与 slice warning 也必须逐条经过宿主路径、凭据、URL 脱敏及长度限制,不能只保护错误分支。
- 权限边界:开放的是 `regenerate / registered resources / playtest` 等产品语义,不是原始最高权限。`regenerate` 只由当前请求最新一条原始 User 消息授权并绑定客户端稳定 `clientTurnId`;模型参数、MCP 自动批准和缺失 clientTurnId 都失败关闭。授权输入先对完整原文做 Unicode NFKC 与撇号规范化,随后整串必须完整匹配审核过的独立立即执行指令,只允许句号/感叹号收尾;不得剥离引号、方括号或代码片段,动作前后也不得携带 brief、条件、否定、选择、确认、费用、延迟或其它文本。复杂风格需求先单独描述,再由下一条独立确认消息授权,不能用开放式 deny 词表推断付费同意。同一进程重复水合相同 stable turn 时,“回合仍在运行”只作为非终态占用提示,不得以该 turn 的稳定 assistant messageId 持久化并覆盖原执行结果。DirectProject 的 cwd、sandbox writable root 与文件批准根只允许 canonical 且非 symlink/reparse point 的真实 `game/`,canonical 项目根的原生 OS 路径字节和权威 manifest `projectId` 经域标签及独立长度前缀编码后共同绑定连接池与 thread 身份;项目根、`assets/`、`.agent/` 不可写,网络关闭,命令、MCP 扩权和额外权限批准全部拒绝。受控 `agc_tools` 只在客户端内部从同一真实 `game/` cwd 反查已校验的 canonical 项目根,不把项目根加入 Codex writable roots。Codex 不获得任意 Tauri invoke、Token/Key/Cookie;`resources` 也只投影稳定身份与相对路径,不返回 prompt、provider route、URL 或绝对路径。
- 验收边界:程序证明切片真实 alpha、可见像素、内容唯一、稳定身份和实际渲染;背景语义、无缝地面、实体含义与绘制尺寸继续由工具图片和双视口试玩验收,不把 prompt 描述外推为视觉事实。game-chat 的有效旧包语义替换仍是独立 replace route,不能复用 Direct 开关或伪装为 missing。
- Direct 恢复 claim:同一 App 实例重复水合相同 stable turn 并收到“仍在运行”时,必须释放该 `projectPath + clientTurnId` 的恢复 claim,且不得写稳定 assistant 终态。后续显式刷新对话可按原身份重新读取或续跑;不新增无界自动重试。
- 严格图集崩溃收口:workflow 在严格图集调用前先持久化 `strictSpritesheetPending` 并冻结底层严格事务覆盖的九项旧合同身份;旧路径可精确冻结为缺失。Provider 完成结果先绑定原 retained stage ledger。恢复在同一项目锁内对账严格事务;只有新九项合同、规范图/背景图替换锚点与 retained spritesheet result 三者一致才补写 `completed`,旧九项合同才允许补偿。旧合同判定、写 `compensating`、恢复两项素材与登记、回读和清锚点必须在同一项目锁内,重启已有 `compensating` 也重新判定;第三种混合、漂移或 foreign result 状态进入 reconciliation。不能在主图集与四切片已整体提交后仍按两文件 rollback 制造混合包;若中断前阶段告警尚未进入 durable completed result,恢复结果追加“原阶段告警无法完整重放”的明确 warning,不静默清空。
- Direct 对话恢复从新到旧扫描全部合法 User 回合,遇到较新已回答回合继续向前,不得丢失更早未回答回合。成功返回时 Rust 已先持久化 assistant,前端冗余 append 失败也不得重跑 Provider;普通错误终态的显式 append 失败后,恢复 claim 必须保持到 React fallback writer 对同一稳定 assistant messageId 的写入明确成功或失败,不能在 writer 尚在途时按旧 `/history` 快照重跑。fallback 成功后释放 claim;fallback 失败时跳过该 writer 的无界迟到重试并释放 claim,后续显式 `/history` 才可复用原稳定 `clientTurnId`。终态收敛后删除 claim,避免长会话无界增长。
@@ -4921,3 +4921,32 @@
- 原因:当前完整模块 init 的 RSS 会超过基础 Compose 旧 `896m` cgroup 上限;内核 OOM kill SpacetimeDB,客户端只看到传输错误,容易被误判为网络竞态。
- 处理:先查 kernel journal 的 `Memory cgroup out of memory` 和目标容器 ID,再把本地/预发完整容器 SpacetimeDB 上限统一为 `2g`;保留 page pool 限制。不要只增加 publish 重试,也不要把 `/healthz` 或首页改成数据库就绪探针。
- 验证:用新空卷完成模块 publish、五服务启动和 Web/API smoke,并确认容器未 OOM、SpacetimeDB 与 API/Nginx 最终 healthy。
## 同一本地项目切换账号后不能继续信任 manifest 远端 ID(2026-08-23)
- 现象:本地资源文件仍存在,但账号 A 生成后切到账号 B,快速编辑、GIF/视频派生、art-spec 下游生成或直连恢复提示画布不存在、无权限或资源不属于当前项目;原请求重试仍失败。
- 原因:本地 manifest 只有一组 `source.canvasProjectId / resourceId / assetObjectId`,旧实现把它同时当作历史来源和当前账号可编辑引用。新的 operation 虽已绑定 B,却会把 A 的 project/resource/object 身份配合 B Token 发出;另一部分链路又把 A 的画布 ID 与 B 按标题选出的项目严格比较。
- 处理:manifest 远端字段只保留历史 provenance。所有远端编辑和派生先解析 `.agent/runtime/external-editor-bindings/` 中当前 principal 的项目/资源 binding;不存在时从本地正式文件按 asset ID、SHA-256、媒体类型和 canonical kind 在当前账号重登记。禁止按标题、manifest ID 或其他账号 committed ledger 自动采纳远端对象。A 的在途 operation 继续留在 A,B 只能开始自己的新操作。
- 并发与升级陷阱:首次 binding 不能用无锁的“先查后建”,否则两个 generation 会各自创建远端项目/目录;进程锁只能压住当前存活客户端,远端创建还必须携带由 binding key 派生的稳定 `Idempotency-Key`,关闭“响应已返回但 sidecar 未落盘”时重启重复创建的窗口。canonical ID 变更也不能只改新请求指纹;必须用本地 asset、路径和内容摘要白名单恢复旧 accepted/committed 账本,避免升级后已付费结果永久无法恢复。
- binding wire 升级陷阱:在仍标 v1 的 struct 上直接新增必填指纹,会让所有已有本地项目的 binding 反序列化失败,重现“画布项目丢失”。必须升为新 schema,用独立严格 legacy wire 只迁移完整通过现役不变量校验的旧文档;不能用 default 缺失字段或在身份校验前回写。
- 在途切号陷阱:只在首次 POST 前校验 session 不足以锁定 principal。获得 `operationId` 后必须先落盘,后续每次 poll、download 和本地 commit 都要复验原 session;同步 commit 的“先校验再写入”仍有切号竞态,必须用会话租约把校验与本地安装线性化。手工 Tauri 入口若使用临时幂等键,也会在 202 后切号时丢失恢复身份。切号后继续用 A Token 轮询或安装 A 结果同样是账号边界缺失。
- renderer / native 两阶段陷阱:只用一个 generation 同时表示 UI 转换和 native CAS,或在 Rust 确认前先替换 committed Token,会让迟到 install / clear 把新账号覆盖回旧账号。正确做法是 auth generation 与 native 只增 generation 分离,所有 native mutation 串行,入队前冻结 token + origin + user,并持续以显式 account / `null` 期望权威对账迟到完成。对账失败不能恢复未确认候选会话,而要清空 committed 会话与 Token。
- auth origin 竞态陷阱:只在登录前持久化服务器选择不等于冻结事务 origin;请求 A 在途时若 UI 改为 B,返回的 A Token 可能被安装到 B origin。登录、hydrate 与 refresh 必须在首个请求前冻结 origin,让 HTTP 链与 native commit 共用该快照;同时按 origin 隔离 refresh singleflight,不能让 A 的 Promise 被 B 复用。
- stale refresh 陷阱:请求在入队前写入候选 Token 后,若 queued commit 直接因 generation 过期返回却不恢复 renderer authority,本地请求会继续携带错账号 Token。另外,A refresh 失败晚于 B 登录成功时,若仍向全局发布 failed,`AuthenticatedClient` 会把 B 误登出。所有 stale early return 先恢复当前 committed / desired Token;旧 owner 的迟到 refresh failure 只返回 `stale`,不 clear、不发布 failed。
- 账本写入租约陷阱:只在进入命令时读一次当前账号,仍可能在 owner 绑定、服务身份指纹 / 挑战或确认落盘前切到 B,从而向 A 账本写入 B 身份。这些写入必须持同一冻结 platform session 租约并精确校验 ledger owner;公开的 request / confirm 命令也必须自身完成 owner 门禁,不能依赖调用方曾经走过恢复流程。
- 恢复列表泄漏陷阱:项目相同不代表账本对当前账号可见。如果列表只按 project / phase 扫描,切 B 后会展示 A 的 operation,迟到回包还可能把已清空的列表重新写回。扫描必须持当前 session 租约并按完整 owner `userId + api origin` 过滤;未绑 / 不完整的远端账本隐藏,纯本地编辑保留。renderer 还要用 auth generation 使恢复 read epoch 失效并清空相关操作状态。
- 追加审计回滚陷阱:Direct 恢复中 `asset.register` 是 append-only 审计,返回错误不能证明 append 未持久。若 file / manifest 已落盘且审计成功或结果未知,删文件或回滚 manifest 会创造“审计已存在、资源却消失”的第二种不一致,重试还可能复制审计或重新扣费。应保留 file + manifest + audit 现场并标记 `reconciliation-required`;后续 binding 失败也使用同一语义。
- Runner 跨 GUI 陷阱:WebView 的 `authGeneration` 会随 GUI 进程重启从较低值重新开始,但 busy Runner 可能仍持有旧 GUI 的高 generation。只用 generation CAS 会把新账号安装误判为过期;只把 OS owner 锁当作授权,或保留独立 `platform.session.install/clear` 入口,又会让 Runner 在单次 IPC 丢失后继续使用旧账号。Runner 协议 v7 由 owner 锁创建随机 epoch,每次会话变更先推进 durable revision claim,并且只允许与 claim 完全匹配的 `runner.attach_gui_owner` 安装会话。Runner 要持续比对 claim,失配立即清空平台会话并拒绝 Runtime 请求;GUI 同步失败还要隔离或停止旧 Runner,不能只向前端报错。
- 验证:用真实平台会话 fixture 覆盖 A→B→重启→A,逐个断言 B 的 URL、请求体和稳定引用中没有 A 的 project/resource/object ID;另测同名 localProjectId 隔离、改名不漂移、源摘要变化、非 refine 参考、视频 committed objectKey、art-spec 派生以及在途 operation 切号零网络。Runner 回归还要覆盖高 generation 旧 epoch 被低 generation 新 epoch 正确替换、迟到旧 epoch/revision attach 失败关闭、claim 改写或同步失败后旧 Runner 零 Runtime 请求。Developer Key fixture 不能替代平台账号隔离证据。
- 关联:`apps/ai-game-creator-shell/src-tauri/src/project/external_editor_bindings.rs`、`project/resource_editor.rs`、`project/asset_canvas/generation.rs`、`agent/generation/canvas_generation.rs`、`agent/direct_runtime.rs`。
## Direct 美术工具不能把“包存在”当成“本次已生成”(2026-08-23)
- 现象:用户明确要求重做美术或切换游戏主题,工具仍立即返回 `assets/art-spec.png`、`assets/direct-game-background.png`、`assets/art-spritesheet.png`;新需求没有 Provider operation,游戏继续使用旧图。切片虽然已经落盘,也可能不出现在资源管理或工具结果中。
- 原因:旧 Direct 工具只有 `brief`,完整包校验成功后无条件短路;固定阶段账本恢复又未比较本次生成 prompt。切片只写文件和切片清单,未作为顶层 manifest asset 投影;工具桥只返回三条主路径并丢失切片与 warning。
- 处理:显式重做使用 `mode=regenerate`,普通请求使用 `reuse-or-create`。重生成必须由当前最新 User 消息明确授权并绑定客户端稳定 `clientTurnId`。授权先对完整原文做 Unicode NFKC 与撇号规范化,随后整串必须完整匹配审核过的独立立即执行指令,只允许句号/感叹号收尾;不得剥离引号、方括号或代码片段,动作前后也不得携带 brief、条件、否定、选择、确认、费用、延迟或其它文本。风格需求先单独描述,再由下一条独立“请重新生成美术”消息确认;不要靠扩充 deny 同义词推断付费同意。同一调用完成回包丢失只从 `completed` 持久结果等值重放,不能因重试再次扣费。App 必须在 Direct 调用前落盘原始 User 消息和回合 ID,Tauri 必须在成功返回前幂等落盘同 ID assistant 终态;同进程重复水合若命中“回合仍在运行”,只能显示瞬时占用提示,不得以稳定 assistant messageId 写成终态并抢占原执行的成功回复。恢复扫描与启动前置恢复必须发现 `resetting / compensating / anchored in-progress` 并在专用锁内恢复,重开项目只续跑真正未回答的原身份。整条付费链必须持有专用跨进程执行锁;换新回合时先持久化 `resetting` 再清理旧阶段账本,不得通过删除 workflow 留出无主窗口。崩溃补偿只恢复旧文件并清 replacement CAS 锚点,已 `prepared / accepted` 阶段账本、原 `Idempotency-Key / operationId` 必须保留,同冻结意图续跑复用旧请求;未知账本在文件 mutation 前失败关闭。只有没有任何阶段账本和替换锚点的孤立 workflow 空壳可原子接管;旧 schema 和其余冲突失败关闭。遇到 prompt 或当前 art-spec 身份不一致的未决账本必须保留原 operation 并返回对账错误。Direct app-server 可写边界只限真实 canonical `game/`,canonical 项目根的原生 OS 路径字节与权威 manifest `projectId` 经域标签和独立长度前缀编码后共同绑定连接池和 thread 身份,不得写项目根、`assets/`、`.agent/`,也不得获得网络、命令、MCP 或权限扩权;受控工具如果需要项目级客户端状态,只能从同一真实 `game/` cwd 经相同校验内部反查项目根,不能扩大模型可写根。标准图集首次创建和重生成都要求四张透明、可见、像素及平台身份唯一的 canonical 切片;工具只回传通过私有回执、公开清单、源图和顶层登记交叉验证的 `slicePaths` 与安全 `resources`。部分/opaque/重复/缺回执切片必须告警,不能把公开清单或顶层自述身份当作 Canvas 权威。
- 验收:不要把规范图当运行态素材,也不要用 prompt 证明图片内容。程序门检查透明/可见像素、唯一性、来源、登记和源码/双视口渲染;背景排除实体、无缝地面、管道或角色尺寸等仍需观察返回图与真实试玩截图。Direct 修复不能外推为 game-chat 已支持有效旧包强制替换。
- 同进程恢复补充:命中“同一 stable turn 仍在运行”后除禁止写 assistant 终态外,还必须删除当前 App 实例的恢复 claim。这样原调用随后成功时显式刷新能读取其终态,随后失败时也能按相同 `clientTurnId` 再次续跑;不要靠重载 WebView 清理进程内 claim,也不要用无界定时轮询制造并发调用。
- 严格图集崩溃补充:规范图和背景图的两文件 rollback 不覆盖严格图集事务已经整体修改的 `.agent/manifest.json`、私有回执、公开清单、主图集、四切片和切片清单。必须在严格调用前持久化 pending 及九项旧合同身份;重启恢复先对账底层严格事务,完整新合同直接收口完成,完整旧合同才补偿前两阶段,混合或漂移状态失败关闭。不要在严格提交成功后局部恢复前两张图。
- 部分旧包补充:rollback 的规范图/背景图必须保存旧字节与旧 manifest entry,不能把这两项缺失隐式当成空内容;显式 `regenerate` 因此只在这两项可信可回滚时开放。历史主图集、私有回执、公开清单或 canonical 切片可以缺失,但八个严格路径与受管顶层 asset identity 必须逐项冻结其真实 `Present/Some` 或 `Missing/None` 状态,补偿也必须恢复相同存在性。不要因为旧美术包缺切片而阻断重生成,也不要把本轮新建的严格文件误记成旧文件。
- 对话扫描与 claim 补充:历史中出现 `User A / User B / Assistant B` 时,B 已回答不代表 A 已回答,扫描必须继续寻找 A。成功 Direct 回复在 Rust 返回前已经落盘,前端冗余 append 失败不能据此重跑;普通错误回复的显式落盘失败时,恢复 claim 要保持到 React fallback writer 的同一 messageId append 明确收敛。writer 成功或明确失败后才释放;失败路径要停止该消息的自动迟到重试,再由显式 `/history` 复用原 stable turn。终态后及时删除 claim,避免 Set 无界增长。
@@ -1,5 +1,16 @@
# AI 游戏创作智能体 App 实施计划
## 2026-08-23 AGC 资源生成补齐(视频 / 动画 / 音效 / 背景音乐)
- 统一资源编辑命令新增 `generationMode=create|derive`:`create` 用于无源生成视频、音效和背景音乐,`derive` 保持现有“基于已有资源派生”。视频新建请求不再携带 `referenceVideoSrcs`;音效 / 背景音乐新建使用同款文本生成端点,但提示词前缀改为“生成新音频”。
- 图片资源新增“生成动画”派生入口:复用 `/api/external/v1/editor/character-animations/generations`(账号模式自动映射 `/api/editor/character-animations/generations`),以源图片的稳定 objectKey、真实宽高和固定 `seedance2.0-fast / 720p / same / 32帧 / 4秒` 参数提交;完成后下载 `previewVideoPath` 预览视频并以 `character-animation` 资源类型登记为本地 MP4/WebM 媒体,避免只保存首帧。
- 本地 manifest 资产模型新增 `imageSequenceFrames` 与 `imageSequenceDurationMs`:角色动画完成时把远端 `frames` 与 `durationSeconds` 随账本持久化,恢复或重新提交后仍写回 manifest,前端资源投影同步暴露。
- 资源页顶部新增“生成视频 / 生成音效 / 生成背景音乐”三个入口,图片聚焦态新增“生成动画”按钮;统一资源编辑面板按 `generationMode` 显示“新建媒体资源”或“编辑现有资源”。
- 新建视频 / 音效 / 背景音乐时,先复用 `prepare_external_canvas_generation_context` 创建或匹配同名画板项目与素材库目录,并在生成请求中携带 `projectId / assetFolderId / canvasCompletion`,结果同时进入平台画布 / 素材库与本地 manifest。
- durable 账本新增 `remoteLegacyPublicPath` 字段(旧账本默认缺省),用于角色动画预览视频的换签下载与恢复;资源提交、轮询、下载、manifest 提交和恢复队列继续复用既有资源编辑事务链。
- 资源编辑请求指纹纳入 `generationMode`,同一 `operationId` 换生成模式重试时不再按旧账本模式恢复;旧账本继续通过 legacy 指纹兼容。
- 去背景(`/api/editor/images/background-removals`)仍缺 External v1 契约,不在本次补齐范围;本次只补视频 / 角色动画 / 音效 / 背景音乐。
## 2026-08-19 UI Editor 节点右键菜单
## 2026-08-20 UI Editor 最终预览互斥子节点
@@ -195,7 +206,7 @@ Supervisor 认领该回执后,由父 run 自己为每个原 delivery 逐一创
- 模式升级:`agentMode` 扩为 `codex_app_server / codex_cli / provider`,新默认为 `codex_app_server`;V1.51 的一次性 `codex exec` 保留为显式兼容模式,HTTP Provider 保留为非 Responses 配置及故障回退模式。
- 进程与节点:External Runner 按“有效 Agent LLM 凭据/Responses 路由 + `projectId/agentId/sessionId/runId`”隔离长期 `codex app-server --stdio`,即每个权威节点 run 直接持有自己的 Codex CLI 子进程与 ephemeral thread,每次完整权威请求映射 turn。同一节点 turn 串行,节点之间进程级隔离;单节点连接失败不得使其它节点同时失去终态。Codex thread 不写 durable recovery;节点完成、重启、retry、handoff 和 finalization 仍只认 AGC 账本。
- LLM 配置:`apiKind` 始终只接受 `openai_responses`;非空 Key 转换为 app-server model provider,base URL 生效,Key 仅走专用环境变量;空 Key 只桥接用户 Codex `auth.json`,不继承环境 `CODEX_API_KEY`。设置面板在 app-server 模式继续显示并保存 model、effort、stream、全局/逐 Agent Key 与路由配置;`openai_chat / anthropic` 明确提示切 `provider`,不得悄悄忽略。`stream=true` 接入 app-server 文本 delta;`webSearchEnabled=true` 因唯一 ToolHost 边界明确拒绝。
- LLM 配置:`apiKind` 始终只接受 `openai_responses`;非空 Key 转换为 app-server model provider,base URL 生效,Key 仅走专用环境变量;空 Key 只桥接用户 Codex `auth.json`,不继承环境 `CODEX_API_KEY`。设置面板在 app-server 模式继续显示并保存 model、effort、stream、全局/逐 Agent Key 与路由配置;`openai_chat / anthropic` 明确提示切 `provider`,不得悄悄忽略。`stream=true` 接入 app-server 文本 delta;`webSearchEnabled=true` 只允许 DirectProject 经客户端审核的 `agc_web_search` 使用,不得启用 Codex 原生 webSearch 或任意网络。
- 安全与取消:临时 cwd、隔离 `CODEX_HOME` 与 OS HOME、read-only、network off、never approval,并在启动前关闭 web/multi-agent/shell/browser/plugin/image 等原生能力;取消从 turn-start pending 阶段就跟踪且只 interrupt 当前 turn。已发送 turn 后连接断开或终态丢失进入 reconciliation,只关闭当前节点进程且不重放同一 request slot;明确 failed/interrupted 不按 transport 重试。
- 资源与退出:app-server pool 按实际凭据快照/base URL/API kind/CLI 版本和节点 run 身份隔离并做有界 LRU;空 AppData Key 必须读取同一份有界 `auth.json` 字节来生成池指纹并桥接隔离登录态,继承的 `CODEX_API_KEY` 始终移除,node thread 也只淘汰 inactive LRU。Runner 正常、强制和 watchdog 退出都显式关池,Linux child 绑定 parent-death signal,防止强杀 Runner 后遗留带凭据孤儿进程。stdout NDJSON 与 stderr 无换行记录均有硬上限;stderr 原文不写入诊断,只记录固定分类、总字节数、SHA-256 和可取得的退出状态。
- 旧配置迁移:既有 AppData 若没有 `agentMode`,只有全局和逐 Agent 路由均为 `openai_responses` 时迁移到 `codex_app_server`;存在 `openai_chat / anthropic` 时显式保留 `provider`,避免打开项目自动恢复时把所有节点批量写成 `invalid-config`。用户确认端点支持 Responses 后,可在设置中显式切换并保留原 model/base URL/API Key。
@@ -396,7 +407,17 @@ Agent Runtime 负责:
- 2026-07-10 补充:后台任务工具箱已加入 `project.restore`。Agent 可在 diff 或自检发现本轮修改走偏后请求恢复到指定 checkpoint;Runtime 复用 `project.restore` 权限策略和项目写锁,observation 只返回 checkpoint id、恢复文件数和删除文件数,不返回本机绝对路径。默认确认策略下不会静默回滚用户项目。
- 2026-07-10 补充:单 Agent 聊天和后台 planning prompt 会读取同一个 Agent 的 Runtime 连续上下文,把本 Agent 最近 status / phase / runId / 当前任务 / 下一步、最近回复、计划、观察、最近 3 条工具动作、最近事件、最近 3 条任务记录和工具策略摘要带入下一轮推理;上下文按规范 taskId 隔离,不读取其他 Agent 的 runtime 文件,并在进入 prompt 前过滤密钥和本机绝对路径。新后台 run 启动时会继承本 Agent 上次 `recentToolCalls` 和 `lastResponse`,让多轮任务不丢失结构化行动证据。
- 2026-07-10 补充:后台任务工具箱已加入 `preview.start`。Agent 可在 loop 中自行请求启动当前项目的本地 HTTP 预览;Runtime 会复用 `preview.start` 策略、项目写锁、共享 `PreviewRegistry`、manifest 预览状态、`.agent/logs/preview.log` 和 run trace 追加逻辑,并把 `agent.runtime.preview.start` 写入 `.agent/agent.db`。该 observation 只向 LLM 返回 localhost URL 与端口,不返回用户项目绝对路径。
- 后台任务工具箱提供 `canvas.asset_generate`。Agent 在 loop 中给出素材 prompt、`outputPath`、比例、尺寸、kind 与展示名;普通已认证客户端通过固定官方 origin 的 `/api/editor/*`、`/api/assets/*` 和 `/api/runtime/external-generation/jobs/*` 调用平台,独立 standalone game-chat/显式高级模式才通过 `/api/external/v1/*` 与自备 Developer Key 调用。生成前按本地项目名称创建或复用同名画布项目和同名素材库目录,请求必须携带 `projectId + assetFolderId + canvasCompletion`,生成结果同时进入平台画布、素材库和本地项目。canonical 视觉 DAG 固定为:`art-director` 用图片生成 `kind=spec` 生成 `assets/art-spec.png`;`design-foundation` 精确引用该 resourceId,用图片生成 `kind=ui-design` 生成 `assets/ui-prototype.png`;`art-asset-plan` 使用同一 resourceId 和具体 `iconDescriptions` 调用图标 spritesheet 生成,产出真实透明的 `assets/art-spritesheet.png`。Runtime 在 POST 前把调用模式、相对 endpoint、精确请求体、SHA-256 与稳定 `Idempotency-Key` 原子写入 `.agent/runtime/canvas-generation-requests/` 私有账本并回读一致;普通模式还冻结 `official-platform-v1 + 官方 origin + ownerUserId`,高级模式冻结 External service origin,两者均不保存 Token 或 Key。`accepted` 账本只恢复 GET;`prepared` 表示首次提交结果未知,只允许使用原 endpoint、原始 JSON 字节与同一键恢复 POST,不得重建远端项目/目录、重组正文或分配新键。普通模式站内响应兼容 `200 + queueState.operationId`、inline 完成和 `job` 状态包装;高级 External v1 固定 `202 + operationId`。生成 POST 使用独立三十五分钟等待预算;game-chat 的两次串行生成纳入父 run `4200` 秒软预算与 `4500` 秒总截止。轮询超时、临时鉴权或网关错误只保留原 operation 并在恢复时继续;结果损坏、状态损坏、透明派生失败或远端完成后的本地提交失败进入对账。`postprocess-failed-source-preserved` 不得登记为透明图集或自动重试;其它 general warning 保持 completed 并与 `sliceWarning` 分别展示。UI extraction 只处理已有带标注 UI 图,不属于这条 DAG;图集不得回退到普通生图。UI 原型 prompt、`generationInputs.artSpec` 和 `ui-prototype.v2` 验收必须从当前项目玩法合同提取 HUD、可玩区域、关键实体、操作、失败/重开与移动布局。旧正式图不合格时只返回 `needs-repair`;Supervisor 认领后由原 owner 使用 `replaceExisting=true` 原位替换。凭据不进入项目文件;幂等键只进入受权限约束的私有生成账本,不进入 observation、manifest、agent.db 或日志。
- 后台任务工具箱提供 `canvas.asset_generate`。Agent 在 loop 中给出素材 prompt、`outputPath`、比例、尺寸、kind 与展示名;普通已认证客户端通过固定官方 origin 的 `/api/editor/*`、`/api/assets/*` 和 `/api/runtime/external-generation/jobs/*` 调用平台,独立 standalone game-chat/显式高级模式才通过 `/api/external/v1/*` 与自备 Developer Key 调用。生成前按当前服务 principal 与本地 `projectId` 的私有 binding 创建或复用远端画布项目和素材库目录,请求必须携带 `projectId + assetFolderId + canvasCompletion`,生成结果同时进入平台画布、素材库和本地项目。canonical 视觉 DAG 固定为:`art-director` 用图片生成 `kind=spec` 生成 `assets/art-spec.png`;`design-foundation` 精确引用该 resourceId,用图片生成 `kind=ui-design` 生成 `assets/ui-prototype.png`;`art-asset-plan` 使用同一 resourceId 和具体 `iconDescriptions` 调用图标 spritesheet 生成,产出真实透明的 `assets/art-spritesheet.png`。Runtime 在 POST 前把调用模式、相对 endpoint、精确请求体、SHA-256 与稳定 `Idempotency-Key` 原子写入 `.agent/runtime/canvas-generation-requests/` 私有账本并回读一致;普通模式还冻结 `official-platform-v1 + 官方 origin + ownerUserId`,高级模式冻结 External service origin,两者均不保存 Token 或 Key。`accepted` 账本只恢复 GET;`prepared` 表示首次提交结果未知,只允许使用原 endpoint、原始 JSON 字节与同一键恢复 POST,不得重建远端项目/目录、重组正文或分配新键。普通模式站内响应兼容 `200 + queueState.operationId`、inline 完成和 `job` 状态包装;高级 External v1 固定 `202 + operationId`。生成 POST 使用独立三十五分钟等待预算;game-chat 的两次串行生成纳入父 run `4200` 秒软预算与 `4500` 秒总截止。轮询超时、临时鉴权或网关错误只保留原 operation 并在恢复时继续;结果损坏、状态损坏、透明派生失败或远端完成后的本地提交失败进入对账。`postprocess-failed-source-preserved` 不得登记为透明图集或自动重试;其它 general warning 保持 completed 并与 `sliceWarning` 分别展示。UI extraction 只处理已有带标注 UI 图,不属于这条 DAG;图集不得回退到普通生图。UI 原型 prompt、`generationInputs.artSpec` 和 `ui-prototype.v2` 验收必须从当前项目玩法合同提取 HUD、可玩区域、关键实体、操作、失败/重开与移动布局。旧正式图不合格时只返回 `needs-repair`;Supervisor 认领后由原 owner 使用 `replaceExisting=true` 原位替换。凭据不进入项目文件;幂等键只进入受权限约束的私有生成账本,不进入 observation、manifest、agent.db 或日志。
- External Editor 的远端项目和资源属于服务 principal,不属于本地项目的跨账号身份。`.agent/manifest.json` 中 `source.canvasProjectId / resourceId / assetObjectId` 只记录生成来源,不得作为当前登录账号的 active editable binding。客户端在 `.agent/runtime/external-editor-bindings/` 私有保存账号级投影:项目键至少包含服务 origin、principal 和本地 `projectId`,资源键再包含远端项目、本地 asset ID、源文件 SHA-256、媒体类型与 canonical asset kind;值保存该 principal 下的远端 project/folder/resource/object 身份。Token 轮换继续复用同一 principal binding,账号、服务、源字节或 canonical kind 变化必须重新建立;同名本地项目和项目改名不得参与权威匹配。
- binding 的 lookup key 与稳定 payload 内容指纹分开校验;远端 project/folder/resource/object ID、object key 或尺寸被单独改写时必须失败关闭。同一本地项目和 principal 的首次项目 binding 建立必须串行化覆盖“复读 binding → 必要的远端创建 → 原子安装 binding”,不得因并发任务创建孤儿画布。External v1 项目、素材目录和项目资源创建请求携带由 binding key 派生的稳定 `Idempotency-Key`;服务端在 owner 与接口命名空间内生成稳定 ID,并允许同键同正文安全重试,使远端响应后、本地 sidecar 落盘前崩溃也不会重复创建。
- 当前 binding wire schema 为 v2,payload fingerprint 必填。已落盘的 v1 project/resource JSON 只能经独立严格 wire 结构读取,在 lookup key、principal、本地 source、远端字段、对象键、时间和尺寸等不变量全部通过后重算指纹并原子回写 v2。不得用 `serde(default)` 把未知或篡改文档混入迁移。
- 已完成的本地正式资源以本地文件和 manifest asset 身份为权威。切换账号后,当前账号没有资源 binding 时,必须从本地字节上传、confirm 并登记到当前账号的远端项目后再编辑或派生;任何请求都不得把旧账号的 `canvasProjectId / resourceId / objectKey` 与新账号 Token 组合。新绑定不得覆盖历史来源,也不得迁移或删除旧账号远端对象。已经 `prepared / accepted / running` 的生成或编辑 operation 仍冻结原 principal、原正文和原 operation;切换账号只能停止恢复并保留对账证据,不能改绑、重放或重复扣费。
- 远端提交返回 `operationId` 后先持久化 accepted 身份,再在每次 poll、download 和本地 commit 前复验冻结会话;同步本地 commit 持有冻结会话租约,使切号只能发生在完整安装前或安装后。Tauri 手工生成同样使用稳定的 durable operation slot,不得使用进程内临时幂等键。历史资源编辑账本从 manifest 远端 resource ID 迁移到本地 asset ID 时,只允许在本地 asset、路径和内容摘要仍一致时兼容旧指纹,新请求不得恢复信任 manifest 远端 ID。
- renderer 中的 auth generation 只用于标记当前 UI 身份转换并使迟到读取失效;发给 Rust / Runner 的 native generation 是另一条只增不减的序列,不得用可重置的 renderer generation 直接充当 native CAS。所有 native install / clear 串行入队;登录、refresh、当前用户查询和 native commit 必须共用首个请求前冻结的同一 API origin,refresh singleflight 也按 origin 隔离。提交入队前冻结候选 `accessToken + api origin + user` 快照,并显式记录期望权威是具体账号还是 `null`。候选快照只在 native 确认成功且 auth generation 仍当前时成为 committed 会话;迟到的 install / clear 完成必须用更高 native generation 把 Rust / Runner 对账回当前期望权威,对账失败则清空 renderer 的 committed 会话与 Token 并失败关闭。
- queued commit 发现 expected auth generation 已过期时,必须在 stale early return 前把 renderer Token 恢复到当前 committed / desired authority,不得留下入队前冻结的候选 Token。旧账号 refresh 的迟到失败若已不属于当前账号,只能投影为 `stale`;不得进入当前账号的 refresh-failed / clear 链路,也不得使 `AuthenticatedClient` 登出已成功切换的新账号。
- 资源编辑与素材画布账本的 platform owner 绑定、服务身份指纹 / 确认挑战和确认结果写入,必须全程持有同一冻结 platform session 租约,并要求账本已有 owner 与该 `userId + api origin` 精确一致;直接调用确认命令也不得越过 owner 校验。Developer Key 路径保持 `owner=null` 且不伪造 platform session 租约。
- 资源恢复列表是当前 principal 的私有投影:列举期间持有当前 platform session 租约,远端账本只在 owner `userId + api origin` 完整且精确匹配时可见;未绑 owner 的 Developer / legacy 远端账本和 owner 不完整账本失败关闭,纯本地编辑仍对项目可见。renderer 在 auth generation 变更时立即使恢复读取 epoch 失效、清空旧列表 / 挑战 / 在途动作并关闭面板,迟到回包不得重新投影旧账号条目。
- Direct 只读恢复中,本地文件与 manifest 登记成功后,`asset.register` 追加审计的结果若为成功或无法判定,都必须保留当前 file + manifest + 已追加的 audit 现场;后续账号资源 binding 安装失败同样保留这些可证事实。该路径统一返回 `reconciliation-required`,禁止回滚 manifest / 删除文件,也禁止在无法证明审计未落盘时重试登记或重新生成。
- 2026-08-03 durable 恢复补充,2026-08-04 扩展 `prepared`:`prepared / accepted / legacy-completed` 恢复必须先从私有账本读取持久化的画布 ID、素材目录 ID、画布名、生成提示词、route、kind 与引用资源;`prepared` 只原样恢复同一逻辑 POST,`accepted` 只查询既有 operation。不得在读取账本前重建请求、重新列举或创建远端项目/目录,也不得让本地输出路径漂移挡住恢复。显式“继续/恢复”复用原 action、pending、provider batch 与生成账本身份,不创建 successor 请求或新幂等键。恢复执行和后续 continuation 使用独立 Tokio task 栈边界,同时继续持有原 Agent lock。终态清理固定先删 generation / parallel 附属 sidecar,最后删 pending 身份锚点;历史孤儿只有所属任务已明确 completed/cancelled 时可自动清理,活动、未知或 `needs-reconciliation` orphan 必须保留并失败关闭。
- 生成账本必须绑定调用模式与服务身份:普通模式为固定官方 origin + ownerUserId,高级 External v1 模式为显式 service origin;Access Token 或 Developer Key 只负责当前请求授权,轮换不得改变已有 operation 的归属。退出或换号提升单调账号 generation,中止并脱离旧请求;旧 owner 账本在新 owner 下禁止 POST、GET、下载、安装和归档,只有重新登录同一 owner 后才能恢复。绝对 signed URL、query/fragment 和未知扩展字段不得进入项目账本,只有安全相对路径或 objectKey 可作为 durable 下载引用。accepted operation 明确 failed 时也保留账本,直到 pending observation 和 Provider batch 成员终态持久化后再按统一清理链删除。首次请求结果已经未知后,鉴权或其它非成功响应不能证明原请求未入队,必须保留原账本。生成账本根目录、扫描与删除使用受控路径解析逐级拒绝符号链接,非法控制路径失败关闭。
- 2026-07-10 补充:后台任务工具箱已加入 `task.list`。Agent 可在 loop 中读取 manifest 任务图、每个 seed task 的状态 / 依赖 / 产物交接,以及按依赖计算的 `readyTaskIds`;Runtime 复用 `task.list` 项目权限策略,策略要求确认或拒绝时只返回策略 observation,不向 LLM 暴露任务图细节。
@@ -407,6 +428,7 @@ Agent Runtime 负责:
- 2026-07-10 补充:后台任务工具箱已加入 `agent.delegate`。Agent 可在 loop 中把明确任务投递到另一个 Agent 的独立后台队列,复用目标 Agent 原有锁和 pending drain 语义;同一目标 Agent 串行,不同目标 Agent 可并行。该工具受 `agent.delegate` 策略保护,策略要求确认或拒绝时不会写目标对话、不会启动目标后台任务,也不会写 `agent.runtime.agent.delegate` 审计记录。
- 2026-07-10 补充:`agent.delegate` 已形成可恢复的父子任务闭环。`delegationId` 由 durable pending action 的 `actionId` 派生,子任务记录会保存 `parentAgentId / parentRunId / delegationId`,终态记录额外保存经过统一凭据清洗和安全截断的 `terminalDetail`;同一委派的提交和回执分别受 delegation 级 OS 文件锁保护,同一目标 Agent 的 runId 分配与 pending 追加还受任务账本 OS 锁保护。子任务进入 `completed / failed / cancelled / budget-exhausted` 任一终态时,Runtime 按 `delegationId` 幂等生成且至多生成一次 `agent.delegate.result` 回执,失败、排队或活跃取消、预算耗尽都必须回传,不能只覆盖成功。回执会向父 Agent 既有队列追加固定 runId、`source=agent-delegate-receipt` 的续跑任务,把完整的已清洗 `terminalDetail` 交回父 run,不再只保留 80 字符 UI 摘要;回执 prompt 明确禁止重复同一委派,排队期间不提前写入父会话,真正开始执行时才幂等落盘,用户消息或回执消息落盘失败时不会进入 LLM。回执任务保留父 run 关联,并在真正开始或恢复前再次检查父 run 状态,关联缺失或父 run 不存在时失败关闭;该续跑仍受父 Agent 原有 FIFO、per-Agent OS 锁、权限确认、取消、恢复和 `needs-reconciliation` 屏障约束,不直接重入父 run、不插队、不新增独立 worker;父 run 已取消或普通失败时只保留 suppressed receipt 审计,不自动复活,父 Session 归档与切换会被未结束委派阻止,极端归档竞态下回执回落到父 Agent 当前可写 Session。恢复先恢复 pending action / reconciliation 屏障,再扫描“子任务终态已落盘但回执未提交”的窗口并补齐缺失回执;`needs-reconciliation` 本身不回执,只有人工核对后最终取消才回传 `cancelled`。
- 历史记录(已由 V1.1 独立 Runner 替代):Runtime 最初通过 `resume_game_creator_agent_runtime_tasks` 把本地 JSONL 队列重接到当前 App 进程。当前恢复入口仍保留权限、任务顺序和 `agent.runtime.background_task.recovered` 审计语义,但实际由独立 Runner 接管原 run / session;已发出的上游 LLM 请求仍不能从网络中间点续传。2026-07-27 起,Runner 归 Tauri GUI 生命周期所有,同一 AppData 只允许一个 GUI owner。GUI 启动子进程会显式声明 `--gui-owner-required` 并在就绪后 attach owner;Runner 若在启动检查前已发现 owner 释放则直接失败,不得退化成 CLI-owned Runner。Runner 使用独立 watchdog 线程每 100ms 监控 owner OS 锁,不依赖服务端主循环继续推进;owner 丢失后先触发 1.5 秒共享 deadline 的 draining、Provider 中断和 process session 回收,若主循环或排空链路卡死则在 1.75 秒后由 Runner 自身进程安全硬退出并清理匹配 bootId 的 endpoint。GUI 客户端还必须把完整 `runner.attach_gui_owner` 参数作为绑定规范化 AppData 的进程内登记保存;`ensure_external_agent_runner` 无论复用既有 endpoint 还是启动新 Runner,都要在把 endpoint 交给 Runtime 写请求前按新 `bootId` 补登记。同一登记 generation 在同一 boot 上幂等,补登记失败不得记录成功 boot 且本次 `ensure` 失败关闭;未建立 GUI 登记的普通 CLI 不执行该重放。OS owner 锁与 watchdog 已成立只代表进程受 GUI 生命周期约束,不能替代事件 sink 等进程内附加能力的逐 boot 恢复。因此正常最终退出、panic、SIGKILL 和 setup 中途失败都不会再因 busy 或主循环卡死而残留后台进程。endpoint 缺失 / 读取失败必须结合 Runner 实例锁判断;GUI 客户端强制兜底在 Linux 使用 pidfd、Windows 使用稳定进程 handle。macOS 没有等价稳定句柄,客户端不得在 start identity 检查后按裸 PID 强杀,而由跨平台 Runner 自身 watchdog 提供硬退出兜底。旧 endpoint 缺 start identity 时,只有认证 ping 精确匹配 PID + bootId 才允许迁移 busy 旧 Runner。未完成任务保持 durable 状态并在下一次启动走 reconciliation / recovery,不能伪造 completed 或重放副作用。关闭单个 WebView / 子窗口和普通 CLI 退出不触发该行为,版本切换与人工命令仍可使用只关闭空闲实例的 `runner.shutdown_if_idle`。
- 2026-08-23 Runner 协议 v7 GUI owner 会话权威补充:GUI 取得 owner OS 锁时产生随机 `owner epoch`,并在私有 AppData 持久化只含 `owner epoch + session revision` 的 claim;每次登录、refresh、退出或换号都必须先单调推进 durable session revision,再同步 Runner。`runner.attach_gui_owner` 是 Runner 接受平台会话快照的唯一授权入口;只有 attach 携带的 epoch/revision 与 durable claim 完全一致才可安装或清除会话,新 GUI epoch 可替换旧进程留下的高 `authGeneration`,不用可在新 WebView 重置的 generation 猜测进程所有权。Runner 在 claim 缺失、不可读或与当前 attach 身份失配时立即清空进程内平台会话,并阻断除重新 attach 及必要管理请求以外的 Runtime 工作;旧 `platform.session.install/clear` 协议不再是授权入口。GUI 会话同步未得到完整 attach 确认时本地变更必须失败,并隔离或停止旧 Runner;即使进程终止失败,claim 失配门禁也不允许旧账号继续发起 Runtime 请求。claim 不保存 Access Token,Token 只随当次受保护的 attach IPC 进入 Runner 内存。
- 2026-08-05 GUI owner attachment 确认补充:登记参数必须保存 GUI manifest 事件接收端的真实 `event_sink_port` 与 `event_sink_token`,不得借用 actionId 等无关字段作为测试替身。每次 attach RPC 只有同时返回 `attached=true` 与 `eventSinkAttached=true` 才能把当前 `bootId` 标记为已登记;`eventSinkAttached` 缺失、为 false 或普通 RPC 失败都保持当前 boot 待重试。sink token 只留在私有进程内登记和 RPC 参数中,不进入日志、错误文本或公共状态。
- 2026-07-10 补充,2026-07-16 由 V1.28 澄清:后台 planning 与预算内 final reply 使用专用最小上下文,只预置 Agent 身份、sessionId、runId、执行模式和工具策略;Agent 私有记忆、项目记忆、黑板、对话、资产、项目索引与文件正文只能经对应工具通过权限 gate 后作为 observation 进入下一轮。只有开发窗口的专业 Agent 前台直调可使用对应角色上下文;正式用户前台现已统一进入 `project-supervisor`。长黑板、记忆和对话按尾部截断,确保最新结论与最新定向消息优先保留。
- 2026-07-10 补充,2026-07-16 由 V1.28 澄清:同一 Agent 的开发前台直调、流式调试和后台任务统一使用 `.agent/runtime/locks/<agentId>.lock` OS 文件锁。开发前台不再在整个 LLM 请求期间占用项目级写锁;同 Agent 后台任务在开发前台运行时只入队,前台成功或失败后把当前 Agent 锁直接移交给 drain,不重新抢锁,也不允许 drain 启动异常把已经完成的调试结果改判为失败。正式用户 GUI 不通过该入口直聊专业 Agent;不同 Agent 继续并行,真实项目写工具只在副作用执行期间短暂申请项目写锁。
@@ -527,7 +549,7 @@ game-project/
- 中间主视窗提供 `resource-overview / asset-canvas / resource-editor / run` 四种状态。2026-08-10 起普通用户“新增资源”显示为禁用态且处理函数拒绝 create;所有现役资源从聚焦态“编辑资源”进入非破坏性派生。静态图片继续进入 refine 素材创作无限画布,SVG、视频、音频、文档/代码、Agent 回执和项目版本进入统一资源编辑壳并按能力分流;底层 create 合同仅保留兼容。编辑面板只替换中央区域,不覆盖右侧 Supervisor 或底部 Agent。`code-prototype` 任务完成前运行入口保持视觉不可用,但仍可点击查看“当前无可运行版本”,不能使用会阻断说明交互的原生 `disabled` 或 `aria-disabled`;完成后才允许进入运行表现层。切回资源总览只修改前端展示态,不伪造后端预览暂停结果。
- 资源管理从当前 `GameCreationAppManifest`(包含可选 `versions`)、合法 Agent 文本回执、已导入附件和已完成任务明确登记的产物派生资源,固定按文档、项目版本、美术资源、音乐音效资源分区;未知任务产物不再兜底为版本,未完成任务或未在 `artifacts` 中登记的任意本地音频也不冒充正式资源。`按依赖 / 按类型` 使用各自前端排列,dependency 模式额外绘制当前 manifest 与资源投影可证明的依赖关系。排列与图层都不写回 manifest,不能推断或伪造缺失依赖。
- 资源卡支持点击聚焦、搜索和类型筛选。2026-07-28 起完成两套二维坐标与本地 CAS sidecar;2026-07-31 起 dependency 模式增加不持久化的原生 SVG 关系图层。2026-08-03 mentor 决定暂缓资源总览卡片拖动,当前卡片不挂载 Pointer Down / Move / Up / Cancel 拖动入口,只允许自动布局和点击聚焦。聚焦态替换中央主视窗内容,保留左侧导航、右侧对话和底部 Agent 状态栏,退出后恢复搜索、布局模式、滚动位置与选中资源;不提供通用工具栏、工具侧边栏或可拖动标题栏。阶段四已补齐安全本地文档、扩展美术媒体与音频聚焦,正文独立滚动,视频 / 音频使用内置媒体控件,失败显示空态。该资源总览边界不限制后续素材创作无限画布内的图片图层移动/缩放、生成和正式回写。
- 运行表现层首版直接嵌入当前项目的 loopback 游戏画面,并展示上一项 / 暂停继续 / 下一项切片控制、素材信息和数值微调面板。`preview.start` 启动本地 server 后把真实 URL 回写工作台,`preview.open` 只激活客户端内运行视图,不再调用系统浏览器;切片、参数调整和自然语言新增调节项首版仍只保留本地 UI 草稿,不修改代码或 manifest。
- 运行表现层首版直接嵌入当前项目的 loopback 游戏画面,并展示上一项 / 暂停继续 / 下一项切片控制、素材信息和数值微调面板。`preview.start` 启动本地 server 后把真实 URL 回写工作台,`preview.open` 只激活客户端内运行视图,不再调用系统浏览器;切片、参数调整和自然语言新增调节项首版仍只保留本地 UI 草稿,不修改代码或 manifest。preview server 对 UTF-8 HTML 响应注入固定同源尺寸桥脚本;注入点通过真实 HTML tokenizer 边界定位,保守处理注释异常结束、DOCTYPE 引号、script escaped / double-escaped、raw-text、template、plaintext、foreign content 与重复 `src`,并支持省略 `</body>` / `</html>`。桥以 `ResizeObserver` 观察 `documentElement / body` 根布局,结合页面 load、窗口 resize 与字体就绪重新测量;页面可见时另以 `500ms` 低频兜底探测至多 `512` 个元素的实际边界,探测截断时保留 body / scroll 上界,并按连续测量排除随 viewport 同步变化的 `100vh / 100% / bottom / right` 自反馈。相同尺寸元组去重后才以固定版本 `postMessage` 上报,不订阅整页 `MutationObserver`。宿主同时校验消息 origin 和 `event.source`,以实际内容宽高与当前容器宽高计算不超过 `1` 的等比缩放;首次适配后仍接受内容宽高的真实变化,但仅 viewport 回灌或重复内容尺寸不更新 React 状态。容器 resize 后回到原生视口重新测量;放得下时保持 `1:1`,超出时完整缩小并居中,iframe 禁止横纵滚动条,不能以 `overflow: hidden` 直接裁掉超出内容。非 UTF-8 HTML 原样返回,不因适配桥破坏已有预览。
- 右侧继续复用现有 Project Supervisor 会话、Runtime 澄清和确认链路;输入区展示 `严格审批 / 风险审批 / 无需审批` 独立面板。P0 只有严格审批可选;风险审批和无需审批保持视觉不可用但允许点击查看原因,不替代 Runtime 的逐动作权限、确认、sandbox 或 reconciliation 门禁。风险 Rank 算法记录在 `docs/project-memory/todos/【待解决】AI游戏创作高风险审批Rank-2026-07-20.md`,前端不得自行计算。
- 底部状态栏默认展示策划、美术、程序 3 组,并允许在同一栏展开数值、音频、发布组;状态来自 manifest 与当前 Supervisor run 的 Runtime,悬停显示当前任务与进度。累计泥点必须等待后端计费归因投影;Agent.md 编辑和自定义 Skill 在来源审核、版本、权限、sandbox 与回滚合同完备前不向普通用户开放。
- 当前 run 专业状态与项目历史成果分离:状态继续严格匹配当前 `parentRunId`;已有文本成果从专业 Agent 持久对话中合法的 `agent-finalization-<32 lower hex>` assistant 恢复,并以“历史成果”来源投影到资源管理文档区。新 run 失败、待确认、候选为空或持久对话瞬时读取失败不得清除已恢复的旧成功回执,普通失败 assistant 也不得被当作成果。
@@ -1130,7 +1152,7 @@ game-project/
- 阶段五只接图片生成与图片精修,通过阶段二共享 `ImageCanvasGenerationPort` 调用客户端 Tauri adapter,并复用现有编辑器项目、统一生成队列、资产上传/换签、稳定引用解析和受控下载能力;不复制网站 `useImageCanvasGenerationWorkflow`,不新增角色动画、视频、音频、图集或平行生成系统。
- 生成面板和确认摘要只操作本地状态;用户点击独立“确认生成”后才通过 `canvas.asset_generate` confirm 权限边界。本地生成意图建立时即分配稳定 `intentId/generationId/idempotencyKey`,取消确认则直接丢弃且零 External 调用;一旦提交,重复点击、重启和未知结果保持原身份,不能自动确认或换键重扣。
- 主站网页画布与普通 AI 游戏创作 Tauri 客户端统一使用当前平台登录态调用固定官方 origin 的 `/api/editor/*`、`/api/assets/*` 与 `/api/runtime/external-generation/jobs/*`。网站 Access Token 延续现有 WebView 客户端存储与刷新 Cookie 机制;登录成功或刷新后,AGC 只把 `userId + 短期 Access Token + 单调 authGeneration` 的当前快照同步到 GUI/Runner 进程内存,退出或换号以更高 generation 清除旧快照。Token 不写 Rust AppData 配置、项目、账本或日志。`/api/external/v1/*` 继续只供第三方开发者和独立 standalone game-chat/高级自定义模式使用自备 `tnr_sk_...`,不自动签发或下发 Developer API Key。
- 主站网页画布与普通 AI 游戏创作 Tauri 客户端统一使用当前平台登录态调用固定官方 origin 的 `/api/editor/*`、`/api/assets/*` 与 `/api/runtime/external-generation/jobs/*`。网站 Access Token 延续现有 WebView 客户端存储与刷新 Cookie 机制;GUI 层的同一 `owner epoch` 内仍使用单调 `authGeneration` 约束请求与迟到回包,但每次登录、refresh、退出或换号先推进 durable session revision,再由与 claim 完全匹配的 `runner.attach_gui_owner` 把 `userId + 短期 Access Token + authGeneration` 快照安装到 Runner 进程内存。新 GUI epoch 不继承旧进程 generation;claim 失配时 Runner 清空会话并阻断 Runtime,不得保留旧账号快照。Token 不写 Rust AppData 配置、durable claim、项目、账本或日志。`/api/external/v1/*` 继续只供第三方开发者和独立 standalone game-chat/高级自定义模式使用自备 `tnr_sk_...`,不自动签发或下发 Developer API Key。
- 每个网络动作在发出前冻结 `ownerUserId + authGeneration`,并在响应解析、账本推进、媒体安装和正式提交前复核当前账号。旧 generation 的迟到响应或清除不得覆盖新账号。普通模式首次 `401` 时,每个 generation 只允许一个共享 refresh;其它请求等待它,但保留各自 deadline/取消。refresh 成功后以更高 generation CAS 安装新 Token,并仅用原 endpoint、原始请求字节和原 `Idempotency-Key` 重试一次;refresh 失败、账号切换或再次 `401` 时停止网络并保留账本,`403` 不触发 refresh。
- 调用模式、精确相对 endpoint、精确请求、prompt、idempotency key、operationId 和稳定产物引用只进入 `.agent/workbench/asset-canvas/generations/` 私有账本。本地参考图 direct-upload ticket 的 host/formFields/policy/signature/临时 Authorization 只存在内存;恢复账本只允许保存稳定 bucket/objectKey。普通账本身份固定为 `official-platform-v1 + 官方 origin + ownerUserId`,高级账本身份固定为 External service origin;均不绑定或保存 Token/Key。换号后旧 owner 账本零网络、零安装,只有重新登录同一 owner 后才能恢复。`accepted/running` 只 GET 原 operation,`prepared` 只精确重放冻结 endpoint、原始请求字节和原幂等键。草稿/manifest/event/日志只投影安全阶段与本地身份。
- 固定状态顺序为 `generation-accepted -> generation-running -> remote-completed -> media-downloaded -> asset-durable-committed -> manifest-projected -> layout-ready -> selected`。远端 completed 后必须先验证稳定引用,再经 `/assets/read-url` 下载并写入阶段三 staging;随后用账本中的固定本地 commit 身份进入既有 journal/ledger 事务。正式提交失败只恢复本地事务,不重新生成或下载。
@@ -1172,8 +1194,8 @@ game-project/
- 普通项目对话只由一个 project-bound Codex app-server thread 执行。客户端系统提示词只放最小工程合同、当前游戏源码有界快照、项目 prompts 和审核 Skill 索引;不再批量读取项目 `.codex/.agents/.hermes` 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 构建结果漂移,语义内容变化时必须同步重算对应清单指纹并提升版本。客户端把审核文件安装到隔离目录后通过 app-server `skills/extraRoots/set + skills/list` 注册并复核,完整正文由 Codex 原生 Skill 机制按意图加载,一层引用只能经 `agc_read_skill_resource` 读取清单内 Markdown。引用路径按平台无关规则拒绝反斜杠、盘符、UNC、绝对路径和 `..`,不能依赖当前宿主的 `std::path` 语义判断其它平台路径。
- DirectProject 只连接客户端内置的 `agc_tools` STDIO MCP,工具固定为审核引用读取、标准陶泥儿美术准备和 desktop/mobile 浏览器试玩。MCP 进程只做协议;真实浏览器和付费 External v1 调用通过随机 loopback 地址回到客户端主进程,因此不复制 GUI 登录态、开发者 Key 或项目路径到模型上下文。三项工具固定自动批准,通用 shell、任意网络、多 Agent、插件和外部 MCP 继续关闭。
- `llm.webSearchEnabled=true` 在 `codex_app_server` 模式下不启用 Codex 原生 webSearch,也不打开浏览器能力;它只把第四项受控工具 `agc_web_search` 加入 DirectProject 的 `agc_tools` 目录。该工具由客户端主进程固定访问 Bing RSS,强制 20 秒超时、禁用代理与重定向、限制查询 400 字符和最多 5 条结果,解析后仅返回去 HTML 的有界标题 / 摘要 / 公网 HTTPS 链接,拒绝 loopback、私网、凭据 URL 和非 HTTPS 结果。搜索摘要按不可信网页内容注入提示词,只能作为资料引用,不能当作用户或系统指令执行;开关关闭时工具不出现在 MCP 目录。
- DirectProject 只连接客户端内置的 `agc_tools` STDIO MCP,基础工具固定为审核引用读取、标准陶泥儿美术准备和 desktop/mobile 浏览器试玩;`webSearchEnabled=true` 时才追加受控联网搜索。MCP 进程只做协议;真实浏览器、付费 External v1 调用和受控搜索通过随机 loopback 地址回到客户端主进程,因此不复制 GUI 登录态、开发者 Key 或项目路径到模型上下文。已登记工具固定自动批准,通用 shell、Codex 原生 webSearch、任意网络、多 Agent、插件和外部 MCP 继续关闭。
- `llm.webSearchEnabled=true` 在 `codex_app_server` 模式下只把 `agc_web_search` 加入 DirectProject 的 `agc_tools` 目录,并作为 app-server 连接池隔离键;关闭时目录与 MCP 环境白名单均不含该能力。客户端主进程只允许固定 Bing RSS 出站请求,禁用代理和重定向,设置 20 秒超时、400 字符查询上限、512 KiB 响应上限和最多 5 条结果;解析后仅向模型返回去 HTML 的有界标题、摘要和公网 HTTPS 链接,拒绝 loopback、私网、带凭据 URL 和非 HTTPS 结果。网页结果始终标记为不可信资料,只能引用,不能作为用户或系统指令执行。
- 陶泥儿生成继续复用既有私有 Key、持久幂等账本、operation 恢复、来源/下载/PNG 解码和 manifest 登记。完整可信图集缺切片可以继续,固定四切片只是推荐路径;凭据失效、来源不明或结果未知时失败关闭,不能自动换 Key 或重新扣费。
- 自定义 LLM API Key 路由只在 DirectHome/DirectProject 经 loopback `/responses` 流式代理转发。代理不注入 Key,只要求请求自带 Bearer,并剥离开发网关错误携带的 `X-Codex-*` ChatGPT 账户额度头,防止隔离 app-server 把 API Provider 误判为余额 0;旧 ToolHost 保持原 Provider 行为。
@@ -1223,3 +1245,19 @@ game-project/
- 统一根 lock 出现 `@tauri-apps/api`、`@tauri-apps/plugin-*` 或 `@openai/codex` 是 AGC workspace 的合法聚合结果,不代表根 H5 或 Desktop 获得这些能力。配置门禁必须按 workspace manifest/lock entry 判断归属。
- npm 默认 hoist。Vite/Vitest 对 `@cubone/react-file-manager` 的已发布 ESM bundle、AGC TypeScript/Tauri CLI 和 Windows `@openai/codex-win32-x64` sidecar 解析必须兼容 workspace 本地与根提升位置,不得硬编码依赖只存在于 `apps/ai-game-creator-shell/node_modules`。
- Windows game-chat release 仍必须核对固定 Codex 文件和 SHA-256 manifest;Linux 根 lock 与 typecheck 通过不能替代 Windows sidecar 打包 smoke。完整安装、锁与 CI 口径见 [`npm workspaces 统一依赖边界`](./【技术方案】npm-workspaces统一依赖边界-2026-08-21.md)。
## 2026-08-23 Direct Codex 美术包显式重生成与切片投影
- `agc_tools.taonier_prepare_game_art` 的请求模式固定为 `reuse-or-create | regenerate`。缺省使用 `reuse-or-create`,完整且可信的本地包继续零付费复用;只有用户显式要求重做、替换或切换视觉风格时使用 `regenerate`,并绕过完整包短路,按规范图、背景图、透明图集顺序生成和原位替换。`regenerate` 的旧包前置门只要求规范图和背景图已经可下载、可解码、来源一致且存在可信 manifest 登记,使两项旧字节与登记可以完整 rollback;历史主图集、私有回执、公开清单或 canonical 切片可以缺失。客户端必须把八个严格路径的实际存在性和摘要,以及其中受管顶层 asset identity,逐项冻结为 `Present/Some` 或 `Missing/None`,不能把缺失状态伪造成空文件或虚假登记。规范图或背景图任一缺失或身份无效时才失败关闭并提示先用 `reuse-or-create` 修复基础素材。
- 显式重生成不放宽 External Editor 幂等与未知态边界。固定阶段已有 `prepared / accepted` 账本时,本次生成 prompt 必须与账本冻结 prompt 一致才可恢复;不一致返回 `platform-generation-result-unknown` 并保留原 `Idempotency-Key / operationId` 对账,禁止把旧结果解释为新意图,也禁止另起付费 POST。
- 整包重生成在首个付费阶段前建立客户端私有 v4 workflow,状态固定为 `resetting / in-progress / compensating / completed`,并同时绑定意图摘要和客户端稳定 `clientTurnId`。专用 `direct-codex-art` 跨进程执行锁覆盖整个付费重生成生命周期,但不持有通用项目写锁等待网络。规范图和背景图替换后立即持久化旧字节、旧 manifest entry 与本轮双 CAS 锚点;任一后续阶段失败时进入 `compensating`,可在进程重启后继续恢复旧文件及旧登记。已成功阶段的生成账本继续保留;`completed` 持久化经脱敏和数量 / 长度限制的完整工具结果,同一 `clientTurnId` 回包丢失时必须等值重放且零新 POST。新的显式用户回合先持久化目标回合所有的 `resetting` workflow,再清理上一轮三阶段账本并转回 `in-progress`,任一崩溃点都不得出现无 workflow 窗口。只有尚无任何阶段账本且无替换锚点的孤立 `in-progress` 空壳允许被新回合原子接管;其余身份冲突、未知版本以及缺少新恢复字段的旧 v2/v3 workflow 均失败关闭,不能用 serde 缺省值把旧状态升级成可执行状态。
- 恢复扫描必须把 `resetting`、`compensating` 和仍带替换锚点的 `in-progress` 识别为可恢复状态,并在 Direct app-server 启动前持有同一专用执行锁完成阶段清理、补偿和中性化。补偿只恢复旧文件并清除本地 replacement CAS 锚点;已 `prepared / accepted` 的阶段账本、原 `Idempotency-Key` 与 `operationId` 必须保留,同冻结意图续跑复用原请求身份,未知账本在文件 mutation 前失败关闭。冻结意图一致但进程 invocation 已变化时允许安全接管本轮;`completed` 则以外层原始 `clientTurnId` 为权威,忽略模型重采样 brief 并等值回放。客户端必须在启动 Direct Codex 前幂等落盘原始 User 消息与稳定回合 ID;最终 assistant 回复必须在 Tauri 成功返回和 `completed` 事件前,以同一稳定回合 ID 幂等写入项目主对话,重启后项目对话只续跑真正未回答的原始回合,不能生成新身份或重复应用已完成代码修改。
- workflow 在调用严格图集事务前必须先持久化 `strictSpritesheetPending`,并冻结严格事务覆盖的九项旧合同身份:`.agent/manifest.json` 中受管 asset identity、客户端私有回执、公开 `assets/manifest.art.json`、主图集、四张 canonical 切片和公开切片清单;旧路径允许按真实状态冻结为缺失。异步 Provider 返回终态后,客户端必须先把脱敏且可恢复的完成结果绑定到原 retained stage ledger,再允许本地严格事务提交。恢复在同一项目写锁内完成底层严格事务对账与 workflow CAS;若九项新合同与当前规范图身份完整一致、规范图/背景图替换锚点属于本轮,且私有回执的 resource/asset/task identity 与本轮 retained spritesheet 完成结果一致,才保留整组新结果并补写 `completed`。若九项仍逐项精确等于冻结的旧合同,严格合同判定、写入 `compensating`、恢复规范图/背景图与登记、回读验证和清除锚点必须全部位于同一项目锁内;`compensating` 重启也必须重新验证旧合同。任一文件存在性、摘要、顶层 asset identity、retained result 或 CAS 处于第三种状态时进入本地 reconciliation,保留 workflow、阶段账本和文件现场,禁止制造新旧混合包或重新付费。恢复若只能证明完整新合同而无法重建中断前尚未持久化的阶段告警,完成结果必须追加明确恢复告警,不能用空 warning 集合伪装为原阶段没有告警。
- 工具完成结果同时返回主包 `assetPaths`、实际成功持久化的 `slicePaths`、安全身份投影 `resources`,并把普通 `warnings` 与 `sliceWarnings` 分开。每张本地切片都以真实 Canvas `resourceId / assetObjectId / taskId` 和源图集 `sourceResourceId` 登记为顶层 manifest asset;同路径替换保留本地 asset ID。严格图集事务继续覆盖主图、四张 canonical 切片、公开切片清单、私有回执和 `.agent/manifest.json`,失败时整组恢复。旧项目缺顶层切片登记时只能由客户端私有回执授权补登记;可编辑的公开切片清单不能单独成为 `.agent` Canvas 身份来源。
- `regenerate` 授权只取当前请求中最新一条原始 `role=User` 消息,并绑定外层稳定 `clientTurnId`;引号或代码中的按钮文案/示例、历史消息、模型自行填写的 `mode`、MCP 自动批准和缺失 clientTurnId 均不能形成付费替换授权。授权判定先对完整原文做 Unicode NFKC 与常见撇号规范化,随后整串必须完整匹配审核过的独立立即执行指令,只允许句号/感叹号收尾;不得剥离引号、方括号或代码片段,动作前后也不得携带 brief、条件、否定、选择、确认、费用、延迟或任意其它文本。复杂风格需求必须先在非付费消息中描述,再由下一条独立“请重新生成美术”确认消息签发授权;不能靠开放式 deny 词表猜测当前付费同意。工具桥只保留授权判定和摘要,不保存或回传用户原文。同一进程重复水合相同 `clientTurnId` 时,“回合仍在运行”只属于瞬时占用状态,前端不得以稳定 assistant messageId 将其写成终态;原执行的成功回复仍由 Tauri 在返回前持久化。DirectProject app-server 的 cwd、sandbox writable root 和文件变更批准根统一收窄到经 canonical 校验且非 symlink/reparse point 的真实 `game/`;canonical 项目根的原生 OS 路径字节与权威 manifest `projectId` 通过域标签和各自长度前缀编码后共同进入 Direct 连接池和 thread 身份,稳定符号链接改指其它项目、同路径重建项目、不同非 UTF-8 路径或内嵌 NUL 的项目 ID 都不能复用旧连接。项目根、`assets/` 与 `.agent/` 不可写,网络关闭,命令执行、MCP 扩权和额外权限申请一律拒绝。受控 `agc_tools` 子进程只能从同一真实 `game/` cwd 经相同权限校验反查 canonical 项目根供客户端内部桥使用,不能把该根加入 Codex writable roots。`resources` 只返回本地 asset/path/kind/media type、Canvas project/resource/asset/task ID 与 reference resource IDs,不返回 prompt、model、provider route、绝对路径、URL、Token、Cookie 或 API Key。
- 成功响应中的 `warnings / sliceWarnings` 与错误响应采用同一脱敏边界:逐条移除宿主绝对路径、凭据与 URL,并设置固定长度上限;非阻断告警不成为绕开错误分支隐私保护的旁路。
- Direct 同进程重复水合若收到“同一 stable turn 仍在运行”,必须释放当前 App 实例的恢复 claim;该结果不落 assistant 终态,后续显式刷新对话可按原 `clientTurnId` 再次读取已落盘回复或续跑,不要求重载整个 WebView,也不启动无界自动轮询。
- 对话恢复从新到旧扫描全部合法 Direct User 回合;较新的 User 已有稳定 assistant 时必须继续寻找更早未回答回合,不能提前结束扫描。普通成功回复或普通错误回复若终态 assistant 持久化失败,同样必须释放当前 App 实例的恢复 claim,使后续显式 `/history` 能以原稳定 `clientTurnId` 重试;claim 只表示当前实例内正在恢复,不能成为磁盘终态的替代品。
- Direct 的运行态素材验收不再把 `assets/art-spec.png` 当作背景、角色、道具或反馈;规范图只作为派生 reference。标准核心图集无论首次创建还是显式重生成,都必须原子取得恰好四张 canonical 独立切片后才算本次生成成功;每张切片必须有真实 alpha、可见像素、唯一规范像素内容及唯一 Canvas `resourceId / assetObjectId`。旧项目只在私有回执与公开清单、当前源图和顶层登记完全一致时投影四条 `slicePaths`;部分、opaque、重复或缺回执状态只返回 warning,不得猜测或伪造衍生素材。
- 机器门只证明 PNG、真实 alpha、非空可见像素、切片像素唯一、稳定平台身份、顶层登记及源码/双视口实际渲染。背景是否混入实体、地面是否无缝、素材语义是否匹配、最终绘制尺寸是否满足玩法仍由 Codex 检查工具图片和 desktop/mobile 试玩截图;prompt 约束本身不算通过证据。
- 本节只修 Direct Codex 工具链。`project-supervisor-game-chat` 对“已有有效包但用户显式要求视觉替换”的 replace route 仍需独立 Goal Contract 与授权设计,不能把语义不兼容伪装为结构缺失,也不在本次变更中放宽。
@@ -755,6 +755,15 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复
- 时间:`completed_at_micros` 必须为正数并固化为 receipt 完成时间;object/resource/asset/binding/canvas 候选的原时间字段与它一起进入 commit SHA-256,重放必须复用原 prepared commit 而不得重新取时。queue job 完成时间与完成事件仍使用 SpacetimeDB `ctx.timestamp`,不信任调用方时钟。
- 索引:主键 `operation_key`;`by_editor_generation_operation_owner(owner_user_id, operation_id)` 仅用于受控定位和诊断,不允许同 owner 跨 operation kind 复用同一 operation ID。
### `editor_idempotent_create_receipt`
- Rust 结构体:`EditorIdempotentCreateReceipt`
- 源码:`server-rs/crates/spacetime-module/src/editor_project_storage.rs`
- 说明:图片画布工程、素材文件夹和工程资源首次创建的私有 durable receipt。主键 `receipt_key` 由认证 owner、接口 namespace 与 API 根据 `Idempotency-Key` 派生的请求记录 ID 做 domain-separated SHA-256 得到;`request_sha256` 覆盖完整规范化 create payload,但不包含每次重试都会变化的请求时间。首次业务行、副作用与 receipt 必须在同一 SpacetimeDB 事务中提交,主键唯一约束负责并发仲裁。
- 重放:同 owner、namespace、key 与同一原始规范化正文重放时,按 receipt 的 `result_record_id` 返回当前业务行,因此工程改名、目录更新或资源元数据后仍不会把当前可变行误判为请求正文漂移;同键异正文返回 `409`。receipt 不随业务行删除,首次结果已删除时重放统一返回 `409` 并拒绝重建;receipt 缺失但请求稳定 ID 已存在同样失败关闭,不能补造 receipt 或重复首次副作用。
- 索引:主键 `receipt_key`;`by_editor_idempotent_create_receipt_owner(owner_user_id, namespace, request_record_id)` 仅用于受控诊断。表为 private,不作为工程、目录或资源 read model。
- 真实事务门禁:运行 `npm run check:editor-idempotency-procedures`,在隔离的 SpacetimeDB 2.7.0 hotfix3 standalone 中发布当前模块,验证工程、素材文件夹和工程资源的同正文重放、异正文冲突、并发仲裁、删除后失败关闭,以及业务行与 private receipt 一一对应且无孤儿;源码字符串断言不能替代该门禁。
### `editor_generation_runtime_identity_rotation`
- Rust 结构体:`EditorGenerationRuntimeIdentityRotation`