合并主分支

解决合并冲突
This commit is contained in:
2026-08-08 10:47:31 +08:00
140 changed files with 11920 additions and 2046 deletions
@@ -1,5 +1,13 @@
# 决策记录
## 2026-08-07 game-chat 使用单主路径按需补齐美术
- 背景:原 game-chat 把 Supervisor 的意图判断之后又硬接为 `design-director / code-director / art-* / code-prototype / preview-*` 固定图。`code-director` 代替程序主 Agent 判断素材缺口,会让“把现有美术资源应用到游戏中”被错误翻译成先生成美术,且美术回执无法天然回到同一个代码 Run 完成接入。
- 决策:Supervisor 只通过 `agent.route_manifest` 持久化用户 intent,统一使用 `audit-existing-first`;整体视觉重做也只是一项 intent,不审计、不固定委派、不生成,也不授权整套美术强制重生成。持久路由后只启动同一根 Run 的 `code-prototype`;它必须先 `asset.list` 审计权威资源和 Canvas 登记,完整覆盖则直接接入且零生成。只有真实缺失 `art-spec` 或核心 spritesheet 时,主 Agent 才能一次委派相应 `art-director / art-asset-plan`child 只写 `assets/**`,不得写 `game/**`,回执由同一主 Run 认领。主 Agent 随后完成接入、`game.static_smoke` 和 desktop/mobile `preview.validate`。升级时,确定性 `code-prototype` Run 只兼容已知的旧版/当前 canonical task 文本;任意其它正文仍按身份冲突拒绝。升级前已经运行的固定 Graph 美术 child 立即失效,所有项目 mutation 和确定性生图计划失败关闭,不能绕过新主 Agent 继续生成。
- 影响范围:仅 `project-supervisor-game-chat` 的单主 route、动态美术 delivery、完成门、恢复和进度投影;普通 GUI / CLI 的 16 节点 manifest DAG 保持原样。
- 验证方式:覆盖路由后只启动 `code-prototype`、既有素材零美术委派、精确缺口才允许单个对应 child、child 的 `game/**`、memory 和 manifest 写入拒绝而 `assets/**` 写入允许、回执恢复同一主 Run,以及主 Agent 的接入、静态 smoke 与双视口试玩;追加真实 scheduler 对旧 canonical task 的同 Run 恢复、旧固定美术 child 及其历史 isolated 后代对 Canvas/memory/manifest/project scope 零 mutation、伪只读 `agent.run_status` 阻断、嵌套美术 child 硬截止对账回执;另跑完整 DAG 非回归。
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`
## 2026-08-03 资源管理阶段七以完整 CI 与可重复界面合同收口
- 背景:飞书资源管理需求的阶段零至阶段六已经分别完成资源卡禁拖、固定资源投影、中央聚焦、安全文档 / 媒体预览、依赖深度与正式版本只读模型;最后需要统一复核需求边界并用当前主分支完整门禁排除集成回归。
@@ -69,12 +77,12 @@
## 2026-08-06 game-chat 固定规则只提供上下文,Supervisor 决定条件 Graph
> 状态:本条已由上方 `2026-08-07 game-chat 使用单主路径按需补齐美术` 决策完全替代,仅保留问题背景;不得把本条的旧节点、旧路由或旧美术替换规则作为当前 game-chat 指令。
- 背景:用户要求把已有美术接入当前俄罗斯方块时,Runtime 在 Supervisor 首次 Provider turn 之前按关键词重置 Graph、选择是否复用美术并自动调度 child;Supervisor 没有固定快车道计划时又直接 `fixed-task-graph-stalled`。结果是模型没有理解和决策机会,`art-asset-plan` 在已有资产与 baseline 门互相冲突时重复空规划直至耗尽预算。
- 决策:关键词、否定/重做信号、占位状态和当前资产探测统一降级为 `advisoryOnly=true` 上下文。game-chat 根 Run 在持久 `GameChatWorkflowDecision` 前禁止启动任何 manifest child,并允许 Supervisor 正常请求 ProviderSupervisor 通过 auto-safe `agent.route_manifest` 选择 `audit-existing-first``regenerate-art`。审计路线首波只启动 `design-director + code-director`;已登记但无效的旧派生视觉不得在这两项审计启动前截断 scheduler。code-director 必须先成功 `asset.list`,并在 Provider 请求中读取 Supervisor 已持久化且经校验的 `authoritative=true` 决策,再提交绑定当前 root/completion contract/revision 的覆盖合同与 `use-existing-art / generate-missing-art / regenerate-art` 路由。Runtime 只校验身份、正式资产、Canvas、私有合同、切片、manifest、缺口和 fingerprint,并执行条件分支,不从关键词替 Supervisor 作决定
- 资产边界:首版缺口槽位固定为 `art-spec` 与原子 `core-spritesheet`。art spec 缺失若使图集的引用/Canvas 合同同时失效,则两个槽位都是真实缺口;仅图集缺失时复用 art spec,只运行 `art-asset-plan`。完整覆盖零生成;显式重做只授权当前 root 下两个正式 owner 原位替换。已登记但校验失败的固定资产按真实缺口交回其 canonical owner,并只在持久路由绑定当前 root 时授权原位替换。art manifest baseline 豁免只读取持久路由,不重新运行关键词分类
- 连续性边界:正式 failed continuation 继续继承原完成合同;普通美术措辞不再按关键词跨历史根 Run 借用具体试玩类型,避免新项目误继承旧 `tetris-v1`。code-prototype 仍须先读取并最小 patch 非占位入口、可见使用四类切片,再经过静态检查和试玩门禁
- 恢复与权限:`agent.route_manifest` 在共享 Rust/TypeScript 命令目录中为 `auto`,否则 autonomous profile 会因不能等待人工确认而拒绝唯一决策动作;仅根 Supervisor 和其 code-director 审计 child 可执行,隔离 Agent 与其它角色拒绝。执行中断只允许按 durable pending identity 幂等恢复,同 Run 已持久决策/路由禁止改写。
- 验证方式:覆盖决策前零 child、用户原句只产生 hint、旧无效派生视觉不阻断首波、首波只有设计/程序审计、code-director 收到真实持久策略、完整复用、仅缺图集、无效已登记资产由 owner 原位替换、重做两个 owner、baseline 豁免只认持久 route、code-director 的 asset.list/coverage/route 完成门、自动权限,以及旧 root/错误 binding/fingerprint/缺口/重复 route 失败关闭。
- 当前替代:关键词、否定/重做信号、占位状态和当前资产探测只形成 `advisoryOnly=true` 上下文。game-chat 根 Run 在持久 Supervisor intent 前禁止启动任何 child;持久动作只记录用户 intent,统一走 `audit-existing-first`,不审计、不生成、不固定委派,也不授权整套美术强制重生成。随后只启动 `code-prototype`,由它成功 `asset.list` 后判断完整复用或精确缺口;完整覆盖零生成/零委派,缺口才允许一个受限美术 child,回执回到同一主 Run。普通 GUI / CLI 的 16 节点 DAG 不受影响
- 连续性与恢复边界:正式 failed continuation 继续继承原完成合同;普通美术措辞不再按关键词跨历史根 Run 借用具体试玩类型,避免新项目误继承旧 `tetris-v1``code-prototype` 负责读取并最小 patch 非占位入口、可见使用四类切片、静态检查和试玩;美术 child 只能写 `assets/**`,不得写 `game/**``agent.route_manifest` 的自动权限、pending identity 幂等恢复以及 root / binding / fingerprint / 缺口校验均按上方现行决策执行
- 验证方式:覆盖决策前零 child、用户原句只产生 hint、持久 intent 后只启动主 Agent、主 Agent `asset.list` 前零美术委派、完整复用零生成、精确缺口单 child、回执恢复同一主 Run、child 写入范围与当前 revision 的接入/静态 smoke/双视口试玩完成门,以及旧 root/错误 binding/fingerprint/缺口/重复 route 失败关闭
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md``docs/project-memory/shared-memory/development-workflow.md``docs/project-memory/shared-memory/pitfalls.md`
---
@@ -127,6 +135,7 @@
- 影响范围:AI 游戏创作 `runtime_driver/task_start.rs``task_queue.rs`、自主构建 continuation 合同、Supervisor 进度卡与相应 Rust/AppSurface 回归;不改变 manifest DAG、Agent catalog、Provider 路由或项目产物合同。
- 验证方式:不预占 child locks,真实一次调度三项首波任务,并在有界时间内证明每个逻辑 Run 至少写入 running/`turn.started`;重复调度不得新增逻辑 Run。前端固定时钟覆盖正常运行、子 Agent 新活动、疑似停滞、各类合法等待与 terminal 冻结。
- 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md``docs/project-memory/shared-memory/pitfalls.md`
## 2026-07-31 图集切片按需编码并批量确认持久化
- 背景:`2026-07-29 图集切片必须受前置容量和有界 CPU 保护` 收口了连通域数量与 CPU 并发,但切片仍在一次循环里全部裁剪并编码,最多 64 份 PNG 字节连同整张 RGBA 同时驻留内存;持久化又按切片逐个调用 procedure,N 片至少 2N 次写入外加一次 cohort 完成,任一片失败都会留下已确认的部分记录。手动拆分入口另有一处重复鉴权:`get_editor_project` 已经取回并定位了来源资源,随后仍走 `parse_editor_reference_image` 按注册 ID 再解析一次,触发全账号项目与素材库扫描。
@@ -139,6 +148,7 @@
- 验证方式:`platform-image` 覆盖 prepare 不编码且 `Send + Sync`、并发编码多个 index 结果不变、累计裁剪像素在编码前拒绝;`api-server` 覆盖切片记录 ID 稳定且按 owner / index 分区、自动路径保留处理超时告警码、上传超时释放内存许可;`spacetime-module` 覆盖批次校验的完整 cohort、重复 objectKey、来源资源同 owner 同 project、部分 cohort 拒绝与重放只在内容一致时复用。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md``docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、本文件 `2026-07-29 图集切片必须受前置容量和有界 CPU 保护`
- 补记说明:本条为事后补写,记录提交 `cf1a02312` 已落地的行为,不改变其任何决策。
## 2026-07-31 AI 游戏创作资源依赖图采用 Rust 只读拓扑与前端派生 SVG
> 状态:其中资源卡 Pointer Move 拖动预览与局部更新验收已由 2026-08-03 mentor 最新决定暂缓;只读拓扑、SVG 派生展示、搜索与选择高亮合同继续生效。
@@ -1239,7 +1249,7 @@
## 2026-06-18 图片画布 Seedance 2.0 参考媒体提交边界
- 背景:`/editor/canvas` 生成视频需要严格对齐火山 Seedance 2.0 多模态参考输入;参考视频若继续走 Base64 / `data:video` 会超过请求体并被上游拒绝,参考音频单独输入和非 Seedance 模型携带参考字段也会违反文档契约。
- 决策:仅 `seedance2.0-fast` / `seedance2.0` 可提交参考图片、参考视频、参考音频;图片 0~9、视频 0~3、音频 0~3,音频必须搭配图片或视频。参考视频只能提交公网 URL、`asset://` 或画板资源 `objectKey`,禁止 `data:video/*`;视频 / 音频上传先走 OSS 直传和 asset*object confirm,前端保存 signed URL 预览但提交优先 `objectKey`,后端统一重新签名给 Ark。Ark body 按 `image_url` / `video_url` / `audio_url` + `reference*\*`role 构造,并显式发送`generate_audio:false`
- 决策:仅 `seedance2.0-fast` / `seedance2.0` 可提交参考图片、参考视频、参考音频;图片 `0~9`、视频 `0~3`、音频 `0~3`,音频必须搭配图片或视频。参考视频只能提交公网 URL、`asset://` 或画板资源 `objectKey`,禁止 `data:video/*`;视频 / 音频上传先走 OSS 直传和 asset*object confirm,前端保存 signed URL 预览但提交优先 `objectKey`,后端统一重新签名给 Ark。Ark body 按 `image_url` / `video_url` / `audio_url` + `reference*\*`role 构造,并显式发送`generate_audio:false`
- 影响范围:图片画布生成视频面板、参考媒体上传工作流、`editorReferenceUploadClient``ImageCanvasGenerationSubmissionModel``shared-contracts``api-server` 编辑器视频 BFF、Lovart 生成类面板文档。
- 验证方式:运行 `npx vitest run src/components/image-editor/useImageCanvasUploadWorkflow.test.tsx src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts src/services/image-editor/editorReferenceUploadClient.test.ts --reporter verbose``cargo test -p api-server editor_video --manifest-path server-rs/Cargo.toml``cargo test -p shared-contracts editor_video_request_supports_seedance_multimodal_references --manifest-path server-rs/Cargo.toml`,并执行 `npm run typecheck``npm run check:encoding``git diff --check`
- 关联文档:`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`、火山 Seedance 2.0 任务创建文档。
@@ -6058,6 +6068,7 @@
- 占位删除与重试:completion 必须读取当前权威 dialog;若删除已先持久化,只跳过画布 layer / dialog 写回,不得使用请求中的旧 placeholder 复活图层,已经成功持久化的 project resource / 账号素材允许保留。若回包时本地占位已删除,前端不得应用完成快照或写历史;现有布局 CAS 没有 deletion tombstone,因此 completion 先提交、删除保存后冲突的极端竞态仍按权威快照收口,绝对“删除意图胜出”留待 targeted delete / tombstone 方案。该路由是 unsafe POST,客户端不得配置 `EDITOR_REQUEST_RETRY_OPTIONS`;请求字节可能已发出后不因 transport 异常或 `408 / 425 / 429 / 502 / 503 / 504` 自动重放,Bearer 中间件在 handler 前拒绝请求后的既有认证恢复继续保留。结果未知时先 GET 权威项目 / 素材快照,由用户显式决定是否再次执行。
- 历史边界:成功加入画布时写一条 `perfect-pixel` 历史,中文标签为“完美像素”,并纳入新增结果保护;撤销不得让派生 PNG 消失。像素处理失败或 completion 因占位删除未落画布时不写该历史。
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md``docs/【图片画布】撤销范围与操作提示方案-2026-07-17.md``docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md``docs/【编辑器】图片画布结构化持久化与迁移回滚方案-2026-07-19.md`
## 2026-07-31 game-chat 每条输出入聊天、试玩后收束与平台图集引用
- 背景:game-chat 的 ready response 之前只作为 transient stream 展示,专业 Agent 的 `final-reply` 只进入各自私有 conversation,刷新或事件 / 轮询重放时项目聊天可能丢失这些输出;自主构建完成后仍可能继续进入发布任务;配置 External Editor API 时,原型 HTML 也可能不实际使用平台生成的 Canvas 美术资源。
@@ -6306,6 +6317,7 @@
- 影响范围:`/editor/canvas``audio-background-music` 面板撤销按钮与其定向测试;不改变单层交换快照语义、canonicalization、提交锁、Suno 契约或后端 Prompt 助手,也不修改状态模型字段,`temporaryPromptSnapshot` 已在公开 dialog 状态中且只在 `completing` / `simplifying` 期间非空。本条不适用于 SFX,V1.0 不改动 SFX 的一键优化与撤销行为。
- 验证方式:按矩阵逐行覆盖初始隐藏、首次与再次 AI 处理期间显示并禁用、成功启用、失败隐藏、手动编辑后仍启用、点击预设隐藏、`submitting` 有无快照的两种表现、解除锁定后恢复,以及连续撤销互换保持启用;并断言处理期间按钮仍在可访问树中且为真实禁用态。
- 关联文档:`docs/【编辑器】画板音乐生成入口设计-2026-06-18.md`
## 2026-08-03 完美像素对账判据改看 dialog 收口状态,网关合成响应归入未知结果
- 缺陷一(对账把真成功判成失败):对账用「同 ID 的 generation-dialog 是否还在权威快照里」判定成败,而服务端成功回填时**保留**该 dialog 并就地改写——`apply_editor_canvas_generation_items``status: "idle"``composerOpen: false`、写入 `generatedLayerId`、清掉 `errorMessage`,该行为另有服务端测试断言 `dialog["generatedLayerId"]` 钉住。所以响应丢失但服务端其实已完成时,判据反向:用户被告知「画布未收到完美像素结果,请确认素材库」,而结果早已在画布上,重做一遍就造出第二份;这条分支还刻意不套用快照,本地也看不到那个新图层。
@@ -6479,6 +6491,22 @@
- 权威性与剩余风险:preflight 不创建锁、reservation 或新表记录;最终 `persist_editor_pixel_art_result_and_return` 仍在同一事务内重复目录、布局、幂等 identity 和 revision 校验。preflight 通过后若目录或画布并发漂移,最终事务仍可能在 PUT 后拒绝并留下无引用 OSS object;彻底消除该 TOCTOU 需要 durable reservation / journal 或事务协调,不在本 PR 的最小修复边界内。
- 契约影响:只新增 SpacetimeDB procedure ABI 与生成 bindings;没有表字段、index、migration、HTTP DTO、路由、状态码、OpenAPI 或 shared-contracts 变化。
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md``docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`
## 2026-08-03 主站与 AI Game Creator 复用单一泥点钱包 Store
- 背景:主站顶部优先读取 dashboard 总额,图片画板独立轮询 dashboard,充值 controller 和 AI Game Creator 又分别保存充值中心明细;不同请求返回时序不一致会让总额与分桶同时显示不同快照,快速生成或切换账号时旧响应还可能回滚余额。
- 决策:在 `packages/shared` 提供依赖注入式 `createProfileWalletStore`,统一保存 `ownerUserId`、完整 `ProfileMudPointBalance`、读取状态与错误,并在每个实例的独立闭包内完成请求合并、尾随补读、generation 失效和 owner 校验。主站注入 `getPlatformProfileRechargeCenter`AI Game Creator 注入 `getClientProfileRechargeCenter`;两端不共享 transport、认证或重试实现。主站顶部、图片画板顶部和“我的”统计只消费同一 Store 快照,总额固定取 `totalPoints`dashboard 的 `walletBalance` 只保留后端兼容,不再作为钱包 UI 数据源。
- 并发与账号边界:同一 owner generation 同时只执行一个余额读取;读取期间的新变化通知在当前请求结束后补读,直到覆盖最后一次通知。切换或退出账号立即清空快照、提升 generation、中止并脱离旧 generation 的 active 请求,新 owner 不得等待旧 transport 收束;不响应 abort 的旧 transport 可以在后台结束,但其结果必须忽略。消费端在 owner 绑定 effect 提交前也必须按当前用户 ID 同步屏蔽 owner 不匹配的快照,不能让旧余额与新账号身份同屏。充值中心读取或 mutation 响应必须携带请求开始时捕获的 owner,owner 不符时忽略。刷新失败保留已有快照;响应缺少 `mudPointBalance` 时进入错误状态,不用总额反推分桶。
- UI 与 mutation:充值 controller 继续保存商品、订单和支付状态,但充值中心响应必须同步写入共享快照,余额 mutation 应在应用响应后再通知一次合并刷新。充值弹窗的余额和分桶由当前 Store 快照覆盖。生成完成、失败退款、兑换码和邀请奖励等事件只发送余额可能变化通知;账号变化同时清理旧充值中心、账单与支付临时状态。`limitedPoints` 只按既有后端快照原样保存,本决策不新增或调整会员限时泥点展示与结算。
- 2026-08-05 审查补充:主站 transport adapter 必须通过既有请求 options 真实透传 `AbortSignal`;页面恢复时相邻的 `visibilitychange / focus` 合并为一次余额通知,并在卸载时清理待执行任务。Store 的 owner 输入统一在边界 trim,活动请求清理同时观察 Promise 成功与失败,不能用无人接收的 `finally` 派生 Promise。
- 2026-08-05 账号隔离补充,2026-08-07 完善 legacy 总额入口:账单读取、奖励码和邀请码兑换使用各控制器自己的账号生命周期 / 请求 revision,不依赖共享 Store owner effect 的提交时序;旧账号回调不得更新新账号 UI、结束新请求或刷新新账号钱包。充值下单、邀请码兑换和奖励码兑换三条写请求还必须共享账号生命周期 `AbortController`,在切号 effect cleanup 与卸载时中止旧 signal,使 `fetchWithApiAuth` 的 refresh 等待和写请求退避立即结束,禁止旧 POST 重试重新读取新账号 Token。AI Game Creator 的账单与充值使用独立 lifecycle,账号切换 render 必须同步屏蔽旧账单、充值和支付状态。充值中心兼容响应暂缺共享明细时,弹窗保留响应自带的 `walletBalance / mudPointBalance`,不把有效总额改写为 `0`owner 匹配的 legacy `walletBalance` 同时可供个人中心统计卡和图片画板顶部等纯总额入口兜底,但 `mudPointBalance`、钱包展开明细和账单分桶继续保持空,不从总额反推或伪造分桶。
- 2026-08-07 审查收口补充:共享 Store 显式保存 owner 隔离的 `legacyWalletBalance`,确保首次生命周期读取旧响应时无需先打开充值弹窗即可展示纯总额;较新的 legacy-only 响应必须原子清除旧 `mudPointBalance`,该字段不能生成分桶。直接余额快照附带单调 operation sequence;充值中心、支付确认和 watch 等异步响应都在请求发起前捕获快照,后发操作先落地后拒绝更早快照回滚。刷新错误只向 UI 暴露稳定中文提示,不透传 transport / 后端实现文案。
- 2026-08-07 lifecycle 所有权补充:主站钱包坚持全应用唯一 lifecycle,由 `AuthGate` 根认证边界绑定 ready user;现役平台壳、图片编辑器和 profile controller 只消费 Store,不重复声明 owner。根边界在账号失效、依赖切换、StrictMode effect replay 和最终卸载 cleanup 时统一 `resetWalletBalance`,中止活动请求并清除 owner、明细和 legacy 总额;禁止在子页面按相同 user ID 各自清理 module-level Store。
- 2026-08-07 refresh 发布隔离补充:`apiClient` 把公开 token 设置与清理视为认证代际变更,共享 `/api/auth/refresh` 只在“代际 + 发起时 token”快照相同时复用。refresh 成功只能以同一快照 CAS 发布新 token,旧账号晚到成功必须拒绝;旧 refresh 的 401/403 也只能在原快照仍当前时清 token。新账号进入新代际后立即发起独立 refresh,不等待也不加入旧账号 Promise。
- 2026-08-05 生成扣退费时序补充:external generation 入队时尚未扣费,worker 领取为 `running` 后的资产操作才预扣,业务失败则先退款再写任务失败态。主站钱包因此以账号下全局 active external task 为轮询生命周期,每轮成功状态读取都通知共享 Store 合并刷新,终态轮同时覆盖成功结算与失败退款;画布内容刷新仍只限当前项目的 `completed`,不因其它项目或 `failed` 刷新画布。
- 验证:共享 Store 覆盖首次读取、尾随补读、旧响应、账号切换、错误保留和错误 owner;主站覆盖 dashboard 与充值中心不一致时三处 UI 仍一致、切换账号清空及 focus 刷新;AI Game Creator 覆盖 adapter、账号 owner 和 focus 刷新。运行定向 Vitest、两端类型检查、编码检查与 `git diff --check`
- 关联文档:`docs/【项目基线】当前产品与工程约束-2026-05-15.md`
## 2026-08-04 AI 游戏项目 manifest 存储与工作台实时投影
- 存储决策:`.agent/manifest.json` 的版本追加不可变约束由同目录持久专用锁保护,读取旧状态、校验版本前缀、安装临时文件和安装后回读必须处于同一临界区;进程内 Mutex 不能替代跨进程文件锁。
@@ -6538,6 +6566,7 @@
- classic script 分析单元把 inline 与无 `defer / async` 的本地 external 正文按 `game/index.html` 标签顺序交错组成 parser-blocking 段,再把 classic external `defer` 按文档顺序放到解析完成后的 deferred 段;不得把 defer-before-inline 误投影为外链先执行。classic external `async` 的下载完成顺序不可静态证明,当前静态门直接失败关闭。带 `src` 标签的 inline body 继续忽略;外部文件仍执行可信普通文件、`game/` 边界、文件数与累计体积门禁,重复标签按浏览器出现次数保留求值位置。
- Canvas 尺寸、可见性、元素绑定和 stylesheet 选择器扫描只消费浏览器可渲染标记;`template / textarea / noscript / title / style / xmp / iframe / noembed / plaintext` 内的 Canvas、标签和样式诱饵全部跳过。活动顶层 stylesheet 与可见标记分开提取,既允许真实 CSS 参与隐藏/尺寸判断,也不把 CSS raw-text 中的伪标签当作 DOM。
- ESM 组合单元按 dependency 初始化先于 importer 顶层求值排列。import reference 的 span replacement 仍基于原 importer 完成,随后把已闭包的 dependency projection 放在 importer 前并对最终单元重跑 parser、semantic、单元 `2 MiB` 与累计投影 `32 MiB` 门禁;循环模块继续按 `(origin module, original root binding)` canonical identity 去重并要求有界固定点收敛。
## 2026-08-04 JavaScript 延迟状态与复合调用边
- 受控异步 callback 的 alias 读取按完整 enclosing invocation 链延迟到各层函数同步收尾,最外层再延迟到当前 job 末尾;callback 写入仍不在注册点同步提交。conditional / assignment expression callee 分别在 test / RHS 求值后建立调用边,`new` 同时执行普通 function constructor 及 alias。
@@ -6579,6 +6608,12 @@
- 根 Supervisor 的 manifest completion gaps 只对 status 已为 Completed 的 seed task执行正式产物与 Canvas 深验;pending/running/failed 本身已经构成完成阻塞,禁止提前扫描后续波次。自动唤醒 200 次瞬态重试预算耗尽后必须写入 `needs-reconciliation`,不能静默返回并留下假运行状态。
- parent wake 的 terminal reconciliation task 是 durable commit markerstate / queue / event / Agent DB audit 是可幂等重建投影;非瞬态 task journal 读取错误直接失败关闭。restart 只在 raw state 具有完整 Agent/task/Session/run/source/profile/binding/task 身份时修复其当前 run;state 缺失、损坏、空对象或关键身份为空时只取 journal 最后 logical run,完整有效的新 Run 阻止历史 marker 覆盖。event/audit 必须完整 payload 唯一匹配,同键冲突或重复失败关闭;旧 task 终态、Runtime 非 waiting 或新 Run 接管时,durable deferred signal 追加 resolved/superseded 后才返回 obsolete。
## 2026-08-07 game-chat Supervisor 意图与单主美术顺序门
- `game-chat-workflow-decision` 升级为 v2。Supervisor 必须把自己理解的用户目标写入非空 `intentSummary``strategy=audit-existing-first` 只提供固定安全执行上下文,不代表用户意图,也不授权生成图片。Runtime 只校验身份、字段和执行边界,不用关键词替 Supervisor 做语义决定。持久决策后只调度 `code-prototype`,普通 GUI / CLI 的 16 节点 DAG 保持不变。
- `code-prototype` 成为 game-chat 唯一主 Agent:先 `asset.list`,资源完整时直接接入且零美术委派,只有真实缺口才临时委派对应的 `art-director``art-asset-plan`child 只写 `assets/**`。两个槽位同时缺失时,`art-director` 的 delivery 必须先被同一主 Run 认领并达到 `EvidenceReady`,之后才允许委派依赖规范图的 `art-asset-plan`;同一槽位不同 actionId 仍不得重复消耗委派名额。生成型回执未认领、证据未就绪或身份不匹配时完成门失败关闭。
- 升级恢复兼容 v1 决策,但不沿用旧责任链:读取时严格复核 v1 fingerprint,从根完成合同的有效任务恢复 `intentSummary`,并保留旧 fingerprint 只用于核对已有 route 身份。旧 `code-director` coverage/route 对当前单主完成门表现为 migration pending;当前 `code-prototype` 必须重新 `asset.list`,再原位写入自己的 coverage/route。这样同一根 Run 可以继续,又不会把旧 Director 审计冒充成主 Agent 本人的完成证据。
## 2026-08-04 静态视觉状态流与可见证据收口
- Canvas、2D context 与图片变量统一按 semantic symbol 记录声明和整体赋值事件;重新指向离屏 Canvas、无效 context 或新图片时立即失效旧视觉身份,只有绘制点可证明的当前状态才作证。
File diff suppressed because one or more lines are too long
+71 -7
View File
@@ -14,6 +14,14 @@
- 关联:相关文件、文档、提交或 Issue
```
## 派生 Debug 会让完整配置经应用状态递归进入日志
- 现象:配置和状态当前没有直接日志调用,但新增一行 `debug!(?state, ...)``format!("{config:?}")` 就能把 JWT、后台口令、支付私钥、OSS / provider key 与 SpacetimeDB token 一次性写入日志及 OTel 留存面。
- 原因:`AppConfig``AppState``AppStateInner` 曾使用派生 `Debug`;状态继续递归格式化多个含配置的 client。即使顶层状态停止下钻,`SpacetimeClientConfig``SpacetimeClient` 的独立手写路径仍会绕过顶层防线。
- 处理:配置和聚合状态只实现封闭的手写安全摘要,不格式化任一自由字符串或含凭据的嵌套 client;`SpacetimeClientConfig` 独立脱敏,`SpacetimeClient` 只复用该安全摘要。不要以默认 `info` 级别或当前零调用点代替代码约束。
- 验证:同一唯一哨兵同时填入全部凭据字段、可能带凭据的 SpacetimeDB URL 和数据库名,逐一格式化 `AppConfig``AppStateInner``AppState``SpacetimeClientConfig``SpacetimeClient`,断言哨兵零出现且安全运行摘要仍存在。
- 关联:`server-rs/crates/api-server/src/config.rs``server-rs/crates/api-server/src/state.rs``server-rs/crates/spacetime-client/src/active.rs`、Issue #148
## Chat 生成预算字段不能按模型名猜测或失败后自动重放
- 现象:同一个 OpenAI-compatible Chat endpoint 调用 reasoning 模型时返回 `Unsupported parameter: max_tokens`;直接把全局请求字段改成 `max_completion_tokens` 后,旧兼容网关又可能拒绝新字段。
@@ -43,18 +51,26 @@
- 现象:用户只说“现在没有用到任何美术资源”,Graph 就在 Supervisor 输出任何计划前自动打开美术节点;或者用户想复用现有素材,Runtime 直接按关键词预完成节点。Supervisor 无固定计划时随即 `fixed-task-graph-stalled`,看起来像模型不理解意图,实际上模型根本没有获得决策机会。
- 原因:同一套关键词函数同时承担 prompt hint、Graph reset、baseline 豁免和历史试玩类型继承,启发式信号越过 Supervisor 成为了控制面真相;main loop 又在 Provider 请求前优先调度 ready task。
- 处理:启发式结果只序列化成 `advisoryOnly=true` 的 Supervisor context。Scheduler 以持久 `GameChatWorkflowDecision` 为首轮前置门;Supervisor 先选审计或整体重做,code-director 再用成功 `asset.list` 和 Runtime 复核的覆盖合同选择复用或精确补缺。Runtime 可以拒绝过期、伪造、遗漏或重复 route,但不得替 Supervisor 补写决定。
- 验证:直接使用用户原句,断言 hint 命中但 manifest 全部保持 pending、决策前零 child、Provider request 包含路由工具;决策后首波只有 design/codecode-director 未完成 asset.list/route 时不能完成;再分别覆盖完整复用、真实缺口和显式重做。
- 处理:启发式结果只序列化成 `advisoryOnly=true` 的 Supervisor context。Scheduler 以持久 `GameChatWorkflowDecision` 为首轮前置门;Supervisor 只持久化用户 intent,随后由唯一 `code-prototype` 用成功 `asset.list` 和 Runtime 复核的覆盖合同选择复用或精确补缺。Runtime 可以拒绝过期、伪造、遗漏或重复 route/delivery,但不得替 Supervisor 补写决定或固定生成美术
- 验证:直接使用用户原句,断言 hint 命中但 manifest 全部保持 pending、决策前零 child、Provider request 包含路由工具;决策后只启动 code-prototype,它未完成 asset.list 时不得委派美术;再分别覆盖完整复用、真实缺口和显式重做。
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/game_chat_fast_path.rs``runtime_driver/main_loop.rs``runtime_driver/task_start.rs``runtime_protocol/autonomous_completion.rs`
## 既有正式产物不能同时被快车道视为已完成、被本轮 baseline 门视为未变化
- 现象:增量任务已有完整美术图集,`art-asset-plan` 每轮都返回零 action 和“已验证交付”,但 completion gate 每轮都报告 `assets/manifest.art.json(unchanged-from-run-baseline)`;最终 child `loop-budget-exhausted`,随后 Graph 和父 Run 失败。日志中没有本轮 Provider request、tool plan 或 action receipt。
- 原因:Graph reset 无差别重新打开稳定的美术 owner 节点;快车道按“当前产物有效”判断完成,owner 完成合同则按“本轮必须修改 baseline 产物”判断完成,两套语义互相冲突。增加 loop 预算、伪造版本号或机械改写 manifest 都不能消除冲突,还会引入 verification loop、字段丢失或错误复用旧主题。
- 处理:关键词和资产探测只作为 Supervisor 的 advisory context,不能直接修改 Graph。根 Run 先持久化 `audit-existing-first / regenerate-art` 决策;前者只启动 `design-director + code-director`,由 code-director 在成功 `asset.list` 后提交权威覆盖合同和精确缺口。Runtime 验证合同后才把已有 owner 投影为 completed,或只打开缺口 owner;根完成门只对持久路由明确复用的 art manifest 忽略 baseline 相同,所有结构、Canvas、私有回执、切片可见使用验收继续失败关闭
- 验证:先断言 Supervisor 决策前零 child、固定关键词不会预完成节点,再覆盖完整复用、仅缺图集和明确重做。还要直接经过父完成门,证明合法持久路由不再出现 art baseline gap,并证明删除切片后覆盖合同拒绝复用;旧 root、错误 fingerprint、虚构或遗漏缺口重复改写路由都应失败关闭。
- 处理:关键词和资产探测只作为 Supervisor 的 advisory context,不能直接修改 Graph。根 Run 先 `audit-existing-first` 持久化用户 intent;即使用户提出整体视觉重做,这也不授权强制重生成。决策后只启动 `code-prototype`,由它在成功 `asset.list` 后提交或建立权威覆盖/缺口 delivery。Runtime 验证合同后才允许已有资产复用,或只打开精确缺口 owner;根完成门继续要求主 Agent 认领回执并完成接入、Canvas、私有回执、切片可见使用和试玩验收。
- 验证:先断言 Supervisor 决策前零 child、固定关键词不会预完成节点,再覆盖完整复用、仅缺图集和明确重做。还要直接经过父完成门,证明合法持久 route/delivery 不再出现 art baseline gap,并证明删除切片后覆盖合同拒绝复用;旧 root、错误 fingerprint、虚构或遗漏缺口重复委派以及 child 写入 `game/**` 都应失败关闭。
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/game_chat_fast_path.rs``apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs`
## 单主 Graph 升级不能只迁移 sidecar,必须同时处理活跃旧 Run
- 现象:v1 decision/route 能迁移,但升级前正在运行的 `code-prototype` 因 prompt 文本变化被 scheduler 判为身份冲突;同时旧 fixed-graph 的 `art-director / art-asset-plan` 仍持有 scheduler binding,可以脱离新主 Agent 继续生图。
- 原因:迁移测试只手工构造了非确定性主 Run,未经过真实 ready scheduler;资源 route 的迁移也没有自动让已启动的旧责任链失效。硬截止处理若仍只接受根的直接 child,还会把新的嵌套美术 child 留在 running,而丢失 reconciliation 投影。
- 处理:确定性主 Run 只白名单兼容已知 canonical task 文本版本,所有其它身份字段继续精确校验;旧 fixed-graph 美术 child 及沿 isolated instance 父链可证的历史后代在计划和所有非只读工具入口失败关闭,只允许当前 `code-prototype``asset.list` 后重新委派。新美术 child 同样使用显式只读白名单,写工具只允许可证明落在 `assets/**` 的文件/patchset 与 `canvas.asset_generate`,不能借 `memory.write``task.create/update` 修改 memory 和 manifest;会认领 delivery 并写 observed 状态的 `agent.run_status` 也不是只读。绝对硬截止显式验证根、主 Agent、delegated art child 的完整 task/binding/delegation 链。
- 验证:用真实 scheduler 恢复确定性 v1 主 Run;把旧 scheduler 美术 child 置为 running,断言 `canvas.asset_generate``memory.write``task.create``task.update``agent.run_status` 均被拒绝;再持久化其历史 `game/**` writeScope isolated 后代,断言恢复执行写操作仍失败且项目未变。对当前合法美术 child 同样验证 memory/manifest 零写入,再让它带在途外部生成命中硬截止,断言状态进入 `needs-reconciliation` 且 pending/batch/外部生成账本原样保留。
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/task_start.rs``runtime_driver/game_chat_fast_path.rs``runtime_driver/main_loop.rs``runtime_tools/file_ops.rs`
## 执行锁移交给未确认启动的异步 future 会制造永久 queued
- 现象:父 Supervisor 与 Runner 一直显示运行中、heartbeat 正常,专业 Agent 已有 `background_task.queued``autonomous_ready_task.scheduled`,对应执行锁也被 Runner 持有,但该 child 永远没有 running journal、`turn.started` 或后续 Runtime event;其它同批 Agent 可能已经完成。
@@ -78,6 +94,7 @@
- 处理:旧 action 继续禁止重放或伪造 observation;旧 child 与父 Run 先真实终态。新 Supervisor continuation 仅扫描同 Session、同 source、同有效任务合同的历史根 Run,并要求对应 ready-task 同时存在 `failed / needs-reconciliation` 记录、最终 `cancelled` 记录和 durable cancel tombstone,才把当前 manifest 的同一 failed 节点恢复为 pending,让 scheduler 创建新 child Run。manifest 的读取、筛选、child 证据重验和写回放在同一项目写锁内;每个 task journal 只读取一次并按 parent Run 建索引。较新的无 child Run 默认阻断旧凭证,只有其 root journal 精确证明为旧 failed Graph 在进入 scheduler 前即失败时才允许向前查找;scheduler 自身失败不得被当成该兼容场景。
- 验证:构造 reconciliation child、人工 cancel tombstone、failed manifest 和终态父 Run,证明同源 continuation 只重排该节点;并列普通 failed 节点保持 failed,完成合同继续继承原任务 SHA 与项目 baseline,旧 pending action 不恢复。追加覆盖“旧 failed Graph 未调度”的中间 Run 可以跨过,而较新的 scheduler failure 即使没有 child journal 也会阻断更老 tombstone。
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs``apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion_contract_tests.rs`
## `timeout_at` 不能替代显式的预算耗尽预检
- 现象:给完美像素加端点级并发闸后,预算已经耗尽的请求仍然能拿到许可,白占一个名额继续去打几轮全账号 SpacetimeDB 扫描,直到下载那步才失败。
@@ -93,6 +110,7 @@
- 处理:把递增封进一个 guard 结构体,递减放在它的 `Drop` 实现里;递增本身用 `fetch_update` 的 CAS,不能用「先读后加」——两个线程同时读到 `max - 1` 各自加一就会越界。拿到资源后立即 `drop(guard)` 让出队列名额,不要让它跟着许可一起活到请求结束。
- 验证:单测覆盖 CAS 边界(满了返回失败且计数不越界、上限为 0 时任何进入都失败),并由独立用例覆盖 guard 离开作用域后的计数归还。预算耗尽路径只断言 `504`,不得通过另一个测试也会修改的进程级 static before/after 来推断“未入队”,也不得用串行锁或 `--test-threads=1` 掩盖隔离问题。
- 关联:`server-rs/crates/api-server/src/editor_project.rs``try_enter_bounded_queue``EditorPixelArtSnapQueueGuard`)。
## Linux 生产脚本门禁不能假设本地也是 GNU userland
- 现象:macOS 本地运行维护页、生产 API 部署和 Rust 产物门禁时,依次出现 `mv: illegal option -- T``mapfile: command not found``/usr/bin/cp` / `/usr/bin/chmod` 不存在,以及 `.rlib` 明明含有 `.o` 却报告“没有可扫描成员”;安全修复计划还会把 `/var/folders``/private/var/folders` 的系统别名误判为用户符号链接。
@@ -282,7 +300,7 @@
- 现象:测试点击“添加素材”后,图层状态已经写入,但立即用 `getByAltText('画布图片:...')` 偶发或稳定找不到图片;前一张图可能通过,紧接着添加的第二张失败。
- 原因:带 `objectKey` 的画布图片通过 `useResolvedAssetReadUrl` 异步获取签名 URL`resolvedUrl` 就绪前不会渲染带 `alt``<img>``user.click` 只等待点击交互完成,不等待 effect 内的换签 Promise;前一张图在后续操作期间出现只是调度时机,不是同步契约。
- 处理:每次点击添加后分别用 `await screen.findByAltText(...)` 等待对应图片可见,再执行依赖该图层的下一步操作;不要用固定 sleep,也不要只等待最后一张图而让前面的断言依赖偶然调度。
- 处理:每次点击添加后分别用 `await screen.findByAltText(...)` 等待对应图片可见,再执行依赖该图层的下一步操作;不要用固定 sleep,也不要只等待最后一张图而让前面的断言依赖偶然调度。完整前端回归并行负载较高时,可只对明确跨越换签 Promise 的目标查询设置局部、有界的 `5_000ms` 超时,不要放宽 Testing Library 全局超时。
- 验证:先精确运行目标用例并连续重复,再运行所在测试文件和完整前端测试;删除场景仍要保留 A/B 都消失、两个删除调用和撤销不恢复已删除素材的断言。
- 关联:`src/hooks/useResolvedAssetReadUrl.ts``src/components/image-editor/ImageCanvasWorldView.tsx``src/components/image-editor/ImageCanvasEditorAssetsIntegration.test.tsx`
@@ -351,6 +369,14 @@
- 验证:画板生成 workflow 测试覆盖 queueState 持续 `running` 到前端等待窗口结束时,不进入 failed、不显示该排队文案、不添加本地临时结果层。
- 关联:`src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.ts``src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.test.tsx`
## 场景队列终态不保证首次项目快照已经收口生成占位
- 现象:游戏场景任务已经显示完成,但画布仍保留 `generating` 占位;场景链路又禁止用本地结果补层,因此当前会话可能一直停在生成中。
- 原因:外部生成任务终态与项目画布投影不是同一个原子观测点。队列轮询先看到 `completed` 后,紧接着的首次项目 GET 仍可能读到同一 `dialogId` 的未收口占位;若调用统一回读函数时没有传 completion dialog ID,函数无法识别该快照仍未完成,也不会执行已有的有界延迟重读。
- 处理:游戏场景队列调用要把本次占位 `dialogId` 传给 `applyQueuedEditorGenerationProject`。首个快照中该 ID 仍为 unresolved 时,只按既有间隔补读一次项目;不追加本地图层,也不把任务终态直接等同于画布投影终态。其他生成类型若要补同类保护,必须分别复现其权威回填时序后再改,不能用本条场景结论替代验证。
- 验证:场景 workflow 用两个连续快照复现时序:第一个保留 `scene / generating`,第二个包含场景结果并把同一占位置为 `idle`。修复前只读一次并超时,修复后依次应用两个权威快照。
- 关联:`src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.ts``src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.test.tsx``docs/technical/【技术方案】图片画布游戏场景生成链路-2026-08-04.md`
## 图片画布历史不能回退当前权威状态或复活后端已删素材
- 现象:生成占位框移动后开始生成,撤销移动会把仍在运行的生成对象恢复成待生成状态;切换到 2K 或改变比例后撤销位置,旧占位框还可能把当前尺寸回退。上传图层落库后,普通移动撤销可能被提示“可能会使图片消失”并永久卡在栈顶;即使安全检查已放行,直接恢复旧图层快照也会丢失刚回填的资源关联。素材库后端删除关联素材后,更早的移动快照还可能把已删图层重新加入并自动保存;修改素材类型虽然界面提示撤销成功,刷新后却可能从仍指向新类型的 resource 回弹。无稳定 ID 的“修改图片”草稿也可能被 target-null 快照直接关闭。
@@ -520,6 +546,14 @@
- 验证:`npm run test -- src/services/image-editor/editorProjectClient.test.ts src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.test.tsx``cargo test -p api-server inline_data_url --manifest-path server-rs/Cargo.toml`
- 关联:`src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.ts``src/services/image-editor/editorProjectClient.ts``server-rs/crates/api-server/src/editor_generation_queue.rs``docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`
## 专用生成契约不能被通用生成接口和任务摘要绕过
- 现象:专用场景接口要求结构化 `sceneContent + stylePreset`,但调用方仍可向通用图片接口传 `kind = scene``assetKind = scene`,用任意完整 Prompt 生成并持久化正式场景;合法场景入队后,任务侧栏还可能显示后端完整规则文本和通用“生成图片”标题,空白素材名则可能回退成完整 Prompt。
- 原因:专用 handler 内部复用了通用图片 payload、队列和 Worker,但公开通用 HTTP handler 没有限制专用身份;任务摘要又无条件优先提取 payload 顶层 `prompt`,素材名默认值只处理了字段省略,没有处理空白字符串。
- 处理:公开通用 handler 拒绝专用 `kind / assetKind`,专用 handler 仍可直接调用内部共享执行函数;队列投影按 `kind = scene` 从权威 `generationInputs.fields[画面内容]` 派生标题和摘要,缺字段时失败关闭而不是回退内部 Prompt,并重新计算历史缓存;专用素材名统一把省略和空白收口为产品默认值。
- 验证:路由测试先证明旁路会越过 HTTP 边界,再断言两种旁路均返回 `400` 且指向专用端点;摘要测试覆盖新任务、历史错误缓存和缺少画面内容三种情况;标签测试覆盖省略、空白、自定义和 80 字上限。
- 关联:`server-rs/crates/api-server/src/editor_project.rs``server-rs/crates/spacetime-module/src/external_generation.rs``docs/technical/【技术方案】图片画布游戏场景生成链路-2026-08-04.md`
## 图片编辑器角色动画必须提交稳定图片引用
- 现象:图片编辑器里对尚未上传的角色图点击 `生成动画` 后,前端或后端返回 `sourceImageSrc 必须先上传 OSS`
@@ -4073,7 +4107,6 @@
- 现象:延迟重试携带比既有行更旧的调用方时间,回填图片序列字段时若无条件写入,会使 `updated_at` 倒退,导致基于时间戳的同步看不到更新或排序错误。
- 处理:同源图片序列字段只允许 `None → Some`,非空冲突失败关闭;发生回填时 `updated_at = max(existing.updated_at, request_timestamp)`。legacy 音频 repair 不派生资源级图片序列或通用时长,重放继续精确匹配。
## 历史钱包消费不能从最近流水或通用订单快照推算
- 现象:后台用户详情要展示累计花费时,直接复用只返回最近 50 条的 `list_profile_wallet_ledger`,或在充值订单每行使用的通用钱包快照里扫描该用户全部流水。
@@ -4180,6 +4213,13 @@
- 验证:覆盖失败根任务“水晶俄罗斯方块”后输入“继续”、连续 successor、跨 Session、跨 source、正常完成后新输入、带具体新需求、既有非占位入口先 patch 后 smoke、初始化占位的俄罗斯方块真实语义、未知玩法失败关闭、纯继续目标缺失、规范图不在运行 DOM/Canvas、真实动作前后 `sequence` 与 RAF 空转。浏览器验收必须同时比较 baseline 玩法关键文本/控件/状态和当前 revision,不能只看 Canvas 非空与三个固定按钮。
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs``apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/game_chat_fast_path.rs``apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/provider_request_builders.rs`
## game-chat 的固定图不能替代主 Agent 对已有美术的理解(2026-08-07)
- 现象:用户要求把已有美术资源接入游戏时,固定 `code-director -> art-director / art-asset-plan -> code-prototype` 图会在缺少主 Agent 审计的情况下启动美术生成,或把“整体重做”错误实现为无条件生图;美术完成后又换了 Run,代码接入、静态检查和试玩无法形成连续责任链。
- 原因:固定节点把“是否需要美术”的语义判断编码为 Runtime 前置流程,`code-director` 成为另一个主控,而不是让真正接入游戏的 `code-prototype` 基于权威资产事实决策;如果再把固定审计策略塞进用户意图字段,Supervisor 的理解也会被 Runtime 规则覆盖。两个素材槽都缺失时若先消耗不可重试的 `art-asset-plan` 委派,其 child 又必然因缺规范图失败,整个 Run 会进入无法补救的死路。
- 处理:Supervisor 用 `intentSummary` 持久化自己对用户意图的理解,固定 `audit-existing-first` 只作安全执行策略,随后只启动 `code-prototype`。主 Agent 先 `asset.list`,完整覆盖就直接使用;仅在事实证明缺少规范图或核心图集时,才建立一个写入范围受限为 `assets/**` 的美术 durable delivery。两槽都缺失时必须先完成并认领 `art-director``EvidenceReady` delivery,再委派 `art-asset-plan`;回执返回同一主 Run 后再接入素材、原玩法语义检查、静态检查和双视口试玩。读取旧 v1 决策时必须复核其旧 fingerprint,并从完成合同绑定的有效任务迁移 intent;旧 `code-director` coverage/route 只能触发当前主 Agent 重新审计和原位替换,不能直接成为新完成证据。整体视觉重做意图同样必须经过这次审计,不能成为绕过资产复用或强制重生成的固定规则。完整 GUI / CLI DAG 不使用该例外。
- 验证:正反向测试同时证明 `intentSummary` 非空且不被固定策略代替、v1 决策与旧 route 同根恢复、完整资产零委派、真实缺口精确委派、两槽缺失时规范图优先、单个活跃 child、`game/**` 写拒绝、`assets/**` 写允许、回执恢复同一主 Run 和最终主 Agent 自验收;不能只凭 Prompt 出现关键词或 manifest 状态投影判通过。
## Tauri beforeDevCommand 失败不等于已启动客户端会自动退出(2026-08-03)
- 现象:旧 worktree 的 AGC Vite 长期占用 `127.0.0.1:3080`marker 仍指向旧 API;新 worktree 启动 game-chat 后,配套后端在新端口 ready,随后 `beforeDevCommand` 因代理 target 不匹配返回非零,终端已经回到提示符,但原生客户端和它启动的 Runner 仍存活。客户端 WebView 实际加载旧 Vite,因此当前 master 的界面优化看起来全部缺失。
@@ -4196,6 +4236,7 @@
- 处理:从当前 root source 的 seed lane 动态解析全部零依赖首波任务,只对这些 child 容忍 hydration `Pending`,后续 code prototype / preview 仍严格要求 Running/Completed。`streaming / ready` 仍要求当前 revision`committed` 回复改为依据 finalization 的稳定身份查询,不随后续项目 revision 失效。
- 验证:覆盖 `design-director / art-director / code-director` 三个 Pending 首波 child 均可投影 Completed、`code-prototype` Pending 仍被拒绝;非流式专业 Agent 在 finalization 前无 stream,提交后形成 committed stream,再推进项目 revision 后仍可查询且正文不变。
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs``apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/response_stream.rs`
## 异步生成结果未知时不能换幂等键重提(2026-07-31)
- 现象:生成提交发生客户端超时、连接中断或响应丢失后,调用方创建新的 `Idempotency-Key` 再提交一次;原任务其实已经入队,最终造成重复生成、重复扣费和重复画布 / 素材库写入。
@@ -4247,12 +4288,26 @@
- 处理:调用方在未认证时不得启动受保护的钱包刷新;可取消的读取要为每轮分配 `AbortController`,新读取先失效并中止旧读取,组件卸载时同时推进 revision、abort 当前请求并清空句柄。所有 `then / catch / finally` 在更新状态前都要检查 signal 与 revision。
- 验证:定向测试覆盖卸载后请求 signal 已中止;同时复跑触发钱包刷新回调的画布生成集成测试和完整前端测试,不能以单文件偶然快速收束代替全量验证。
## 账号级轮询和并发 bootstrap 必须中止整条旧生命周期(2026-08-06)
- 现象:任务列表 `Promise.all` 一侧失败后,另一侧请求可能跨过重试和卸载继续悬挂;微信充值第一次确认返回 pending 后切换账号,旧订单的延迟重试可能使用新账号 Token 再次请求,401 路径还会影响新账号登录态。
- 原因:只用 React state 或最终回调里的 owner 判断,无法阻止已安排的 timer、下一次 HTTP 请求和同轮未完成分支继续执行;每轮重试覆盖单个 controller ref,也会遗失更早的悬挂请求。
- 处理:并发 bootstrap 每次 attempt 使用独立 `AbortController`,任一分支失败时先中止同轮 controller 再安排有界重试,卸载时中止当前 attempt。充值订单从创建成功起持有同一个 owner、账号 revision 和 `AbortController`;每次 delay、confirm 和 SSE watch 前后都校验生命周期,并把同一 signal 传到请求层;账号切换和卸载先 abort,再清理 ref、state 与旧支付回调 hash。
- 验证:bootstrap 用例覆盖“一侧 reject、另一侧 pending、重试后卸载”,并断言每轮 signal 都已中止;充值用 fake timer 证明首次确认 pending 后切换账号会中止 signal,推进全部退避时间也不会产生第二个确认请求或清理新账号 Token。
## 中止 refresh 等待不等于隔离 token 发布(2026-08-07
- 现象:A 账号的写请求 401 后开始共享 refresh,随后切换到 B。A 的 `AbortSignal` 虽然让业务请求立即结束且不再重放 POST,但底层 refresh 为了其它共享等待者不会被中止;A 的成功回包晚到时仍可能覆盖 B 的 token。
- 原因:只对 `await` 叠加 abort 保护了调用链,没有给共享 Promise 的归属和最终 token 写入加账号栅栏;单一全局 Promise 还会让 B 加入 A 已在途的 refresh。
- 处理:公开 token setter / clearer 每次都推进 auth generationrefresh 按 `generation + 发起时 access token` 共享、并以该快照 CAS 发布成功 token。快照已过期时成功回包转为失效结果,401/403 也不得清理新代际 token;新代际建立自己的 refresh Promise,旧 Promise 收尾时不得清掉新尝试。
- 验证:`src/services/apiClient.test.ts` 要等旧 refresh 完整收束后断言 B token 不变,并用两个独立 deferred response 证明 B 会发起第二个 `/api/auth/refresh`;另覆盖旧 refresh 401 晚到不清 B token。
## 下游 manifest 回调测试不能冒充实时数据源(2026-08-05)
- 现象:工作台的资源、任务与版本重投影单测保持绿色,但后台 Agent 已更新 `.agent/manifest.json` 后,打开中的工作台仍长期显示旧快照,只有重开项目才更新。
- 原因:测试 Supervisor 直接调用 `onManifestChange`,只证明 `App manifest -> WorkspaceLauncher -> ProjectDevelopmentView` 的下游桥接;真实 Runtime event 没有失效字段,监听器也没有重读 manifest。External Runner 又与 GUI 分属不同进程,Runner 内无法使用 GUI `AppHandle`,只补普通 Tauri event 仍不能形成生产链路。
- 处理:后台 manifest mutation 收敛到共用 Runtime emitterGUI 内进程用带 `manifestInvalidated` 的 Runtime updateExternal Runner 通过 GUI owner attach 登记的受令牌保护 loopback sink 转发专用失效事件。App 对当前项目做 single-flight manifest 重读,并以 mounted、项目路径和 scope version 丢弃迟到结果;WorkspaceLauncher 继续只消费完整 manifest 快照,不新增平行状态或轮询。
- 验证:集成测试必须渲染真实 `App + WorkspaceLauncher`、捕获真实 Tauri listener,让 `get_local_game_manifest` 从旧快照切换到新快照,并由非 Supervisor Agent 事件驱动资产、completed 任务、运行入口和版本卡出现;另测项目切换时旧请求迟到。旧的直接 `onManifestChange` 测试只能标记为下游桥接证据。
- 验证:集成测试必须渲染真实 `App + WorkspaceLauncher`、捕获真实 Tauri listener,让 `get_local_game_manifest` 从旧快照切换到新快照,并由非 Supervisor Agent 事件驱动资产、completed 任务、运行入口和版本卡出现;另测项目切换时旧请求迟到。测试夹具必须先等待目标 Tauri listener 注册完成再发失效事件,并对“事件 -> manifest 重读 -> 工作台重投影”使用局部、有界的 `5_000ms` 等待,避免并行全量回归把监听注册或异步投影调度误判为功能失败。旧的直接 `onManifestChange` 测试只能标记为下游桥接证据。
## React 资源详情焦点不能依赖重建对象身份(2026-08-05)
@@ -4307,6 +4362,7 @@
- 现象:parent wake 的 200 次瞬态预算耗尽后 Runtime 仍长期显示 running,或 lane 忙、取消、child 前进、manifest 损坏时 reconciliation 被静默丢弃或覆盖新状态。
- 处理:预算耗尽错误必须向上传递;lane 忙先持久化 deferred signal,再在 lane + 项目锁内重检最新事实。结构损坏路径使用不依赖 manifest hydration 的专用 journal/state 写入,CAS 失败转为继续对账,绝不覆写并发取消或 DAG 进展。可解析的空对象/空 runId 仍是损坏身份,只有完整有效的新 Run 才能阻止旧 markerevent/audit 的同键记录必须完整比对并拒绝冲突或重复。旧 task 已终态、Runtime 非 waiting 或新 Run 接管时,deferred signal 必须写 resolved/superseded,不能留给后续 wake 永久重复 settle。
- 测试注意:autonomous child fixture 先 linked Pending、后正式 Running;终态 runId 必须拒绝复用。判断 Completed-only 诊断时按每个 seed task 的实际状态分析,不能因为 `code-prototype` Pending 就忽略已经 Completed 的 `art-asset-plan` 深验。
## macOS 安全路径测试必须使用规范化临时目录(2026-08-05)
- 现象:调用仓库上下文、Runtime context bundle 或 pending recovery 的 Rust 测试在 macOS 报“Repository root and its ancestors must not be symbolic links”,Linux CI 却可能通过;本地 HTTP 恢复夹具在完整串行测试中还可能偶发 `WouldBlock`
@@ -4335,6 +4391,7 @@
- 原因:把 dialog / canvas 的完整 UI 所有权同时用于账号级钱包和账号内项目级任务列表,或者任务列表只比较 project ID,没有校验账号。
- 处理:按副作用分层校验。钱包只比较账号;任务列表比较账号加项目;dialog、canvas、asset 和 layer 写回继续比较账号、项目、scope version 与原 dialog。正式请求已接受后,删除 UI 状态不等于取消后端任务。
- 验证:分别覆盖删除 dialog、同账号切项目、账号 A 切到账号 B 且 project ID 保持相同,以及原账号原项目原 dialog 仍有效的正常回写。
## GUI owner 锁不能替代逐 boot 的事件接收端登记(2026-08-05)
- 现象:GUI 首次启动后 manifest 事件转发正常,但 Runner 被替换为新 boot 后只剩 owner 锁和 endpoint 可用,后台更新不再到达 GUI;或者 attach 响应只确认 owner,客户端却误记当前 boot 已完整登记,后续 ensure 不再重试。
@@ -4383,3 +4440,10 @@
- receipt 存在不等于引用 object 仍然可信:省略 candidate 的已登记 object 在重放时也要回读 owner/key/task/kind/媒体身份。同时先查同 operation ID job,存在 job 却漏传 completion 必须整笔回滚,否则会得到 receipt 成功而 job 仍 running 的永久分裂。resource/asset/binding 也不得仅核对 object ID/key,必须按 operation 合法 tuple 交叉验证业务元数据。
- 验证:故障注入覆盖 resource 后 asset/binding 失败、canvas CAS 冲突、过期 lease、同 operation 异 fingerprint / 异 commit、receipt 缺失的部分既有记录、精确既有 object 复用与 object 内容漂移;成功重放必须证明记录数、时间、binding/job 事件数和 canvas revision 全部不变。
- 关联:`docs/technical/【后端架构】编辑器生成结果原子提交与幂等重放方案-2026-08-06.md`、Issue #134
## 付费生成不能把素材目录归属校验留到 provider 之后(2026-08-07
- 现象:登录用户给自己的合法 `projectId` 搭配不存在或属于其他账号的 `assetFolderId`,图片、改图、图集、UI 提取、视频、角色动作或音频生成会先扣泥点并调用付费 provider / OSS,直到创建 `editor_asset` 才拒绝目录;失败退款让用户成本归零,平台侧 provider 和存储成本不可逆。
- 原因:`normalize_generated_asset_folder_id` 只处理 `project`、旧 `folder-*` 和默认目录 ID 的兼容映射,不读取 SpacetimeDB;真正的 `require_owned_asset_folder` 位于生成结果持久化末端。把“失败会退款”误当成副作用补偿,漏掉退款不能撤销 provider 请求与 OSS PUT。
- 处理:所有付费编辑器生成在队列 enqueue 前和 worker / inline 执行前复用只读 `preflight_editor_generation_target_and_return`,按认证 owner 校验可选项目及归一化目录;读取失败和归属不匹配一律失败关闭。helper 返回 canonical 项目与目录并覆写后续入队 / worker / 原子准备使用的 payload,不能校验 trim 后的项目却持久化原始空白值。角色图片、角色动作、图标 spritesheet 与 UI 提取省略目录时按实际默认目录预检;默认目录允许尚未创建,自定义目录必须存在且 owned。预检不替代最终 procedure 复验,也不保证跨外部调用的目录锁定。
- 验证:源码顺序回归必须覆盖图片生成、图片修改、图标 spritesheet、UI 设计图提取、视频、角色动作、SFX 与 BGM 的 enqueue / direct 两层,证明纯本地格式和 `data:` / `blob:` 稳定引用门禁先执行,canonical target 在预检后写回 payload,远端引用解析、generation input rebuild、扣费、入队、provider 与 OSS 均留在预检之后;模块侧扫描证明预检只调用 runtime identity、项目、目录只读校验且不含 insert / update / delete,并覆盖带空白项目、`project`、旧 `folder-*`、默认目录 ID、自定义目录与 `None` 归一化。
@@ -47,6 +47,7 @@
- 新增 Markdown 文档时,文件名必须以分类标签开头,格式为 `【标签名】中文标题-日期.md`;只在任务需要时重命名历史文档,避免无关大 diff。
- 涉及中文文本时注意 UTF-8 编码和乱码排查。
- 涉及后端时遵循 DDD 分层,不把业务真相下沉到前端或临时兼容层。
- 运行时日志禁止对完整配置、应用状态或 provider client 做递归 `Debug` 输出;当前 `AppConfig``AppState``AppStateInner``SpacetimeClientConfig``SpacetimeClient` 必须保持封闭的手写安全摘要,并用唯一哨兵测试锁定顶层与可独立格式化路径。其它仍使用派生 `Debug` 的历史 provider 类型不得新增整对象日志调用,后续按类型独立脱敏。新增字段默认不进入摘要,确需排障时只增加枚举、数值、布尔值或是否配置等非敏感字段。
- `packages/shared` 用于前后端 DTO、公开契约及跨页面复用的无业务真相 UI 组件和纯工具;不得把领域规则、后端副作用或正式状态放入其中。
- 修改 `/api/external/v1` 的路由、HTTP 方法、请求 / 响应 DTO、请求头、状态码、鉴权或异步语义时,必须同批更新 `docs/openapi/genarrative-external-v1.openapi.json` 和对应契约测试;Rust 实现与 OpenAPI 未对齐时不得完成、提交或发布。
- `maincloud` / `Maincloud` / `MAINCLOUD` 相关代码、脚本、测试、环境变量、命令和文档要求均视为历史残留,禁止新增、运行或引用;API smoke 统一使用 `npm run dev:api-server``/healthz`