实现资源画布布局持久化 #116

Merged
kdletters merged 13 commits from codex/canvas-layout-persistence into codex/ai-game-creator-app 2026-07-31 12:00:48 +08:00
Contributor

冻结资源画布布局数据与 CAS 合同
实现双模式本地 sidecar 安全读写
接入二维拖动、默认排版和跨重启恢复
补齐并发冲突、安全边界和界面测试
同步技术文档与共享决策

冻结资源画布布局数据与 CAS 合同 实现双模式本地 sidecar 安全读写 接入二维拖动、默认排版和跨重启恢复 补齐并发冲突、安全边界和界面测试 同步技术文档与共享决策
menghao self-assigned this 2026-07-28 15:22:43 +08:00
menghao added 1 commit 2026-07-28 15:22:43 +08:00
实现资源画布布局持久化
Project CI / Backend tests (pull_request) Successful in 3m58s
Project CI / Frontend tests (pull_request) Successful in 4m4s
Project CI / Native shell tests (pull_request) Successful in 12m26s
Project CI / Repository checks (pull_request) Failing after 40s
4c503b7fc3
冻结资源画布布局数据与 CAS 合同
实现双模式本地 sidecar 安全读写
接入二维拖动、默认排版和跨重启恢复
补齐并发冲突、安全边界和界面测试
同步技术文档与共享决策
menghao requested review from kdletters 2026-07-28 15:22:43 +08:00
menghao marked the pull request as work in progress 2026-07-28 15:39:32 +08:00
menghao added 1 commit 2026-07-28 15:42:10 +08:00
修复资源画布布局 CI 检查
Project CI / Repository checks (pull_request) Successful in 1m16s
Project CI / Frontend tests (pull_request) Successful in 2m32s
Project CI / Backend tests (pull_request) Successful in 2m48s
Project CI / Native shell tests (pull_request) Successful in 10m53s
61f1306fee
移除资源布局 Hook 的冗余 useMemo 依赖
按仓库规则整理工作台测试类型导入
menghao marked the pull request as ready for review 2026-07-28 16:00:59 +08:00
kdletters added this to the 陶泥儿gameAgent project 2026-07-29 15:29:54 +08:00
kdletters requested changes 2026-07-30 14:08:19 +08:00
Dismissed
kdletters left a comment
Member

结论:请求更改。

评审固定在 base 5479559c86e4881d51b80a3e60e4c64dac328d0e、head 61f1306fee380d426eb9c5daeff47670f633542a、merge-base d29240b954c7f1acf70bd3023443f68a778e65e1

  1. [P1] 先解决与当前目标分支的内容冲突。git merge-tree 与 Gitea 均确认 apps/ai-game-creator-shell/src/view/project-development/index.tsxdocs/project-memory/shared-memory/decision-log.md 冲突。前者不能简单保留 PR 版本:base 已把 loopback 预览收口到 LocalGamePreviewFrame,rebase/merge 时需要同时保留该收口与本 PR 的布局能力。当前状态无法合并,也无法验证合并后的真实行为。

  2. [P1] 不要让资源集合变化取消本窗口正在进行的布局写入。useProjectResourceCanvasLayout.ts:163-170 的初始化 effect 同时依赖 resourcesfallbackresourceSignature 和捕获 resourcespersistLayout;manifest、附件或 Agent 结果更新会在 120-123 递增 token 并清掉 saving,使旧保存回包在 91-92 被静默忽略,同时重新读取旧 revision。若新增资源又触发自动协调写入,它可能先赢 CAS 并落盘“旧坐标 + 新资源”,刚完成的用户拖动随后冲突但因 token 过期无人处理。无 Tauri bridge 时同一路径还会直接用 fallback 重置当前会话拖动。请拆开 project/mode 首次读取与资源协调,并串行化本窗口写入;资源数组身份不能取消已发出的保存。

  3. [P1] 默认排版在合同允许的 4096 项上会冻结主线程。resourceCanvasLayoutModel.ts:72-114 每探测一个 slot 都线性扫描已有位置,162-175 又对每个新资源从头执行;同一 section 从空布局协调 N 项时接近 O(N^3)。在本 head 上,type 模式 4096 项实测约 9.5 秒,而该函数会在 render fallback 和读取回包中同步运行。请使用占用集合/行列索引或空间索引,将上限规模降到可接受复杂度,并加入接近 4096 项的性能回归测试;dependency 同层布局也需要覆盖。

  4. [P1] stale 布局锁的回收会破坏“同 revision 双写最多一个成功”的核心合同。resource_layout.rs:42-53,96-104 只按 mtime 判 stale,未检查 token 中 owner PID 是否仍存活;而 stale 检查与 remove_file 也不是同一原子认领。两个竞争者可同时判定旧锁 stale:一方删除旧锁并创建新锁后,另一方仍可删除这把新锁,最终双方都进入 read-check-write。请加入活 owner 保护,并让 stale reclaim 对被检查的同一锁实例做安全认领,补同步竞争测试。

  5. [P2] 无效项目路径不应先产生目录副作用。resource_layout.rs:278-280 在读取 .agent/manifest.json/确认 projectId 前先 acquire lock,而 58-61create_dir_all。因此任意不存在或非项目的绝对 projectPath 虽最终报 manifest 错误,却已经创建 <path>/.agent/workbench/resource-layouts/。请先只读确认有效项目,锁内再复核身份,并补 invalid/nonexistent root 零副作用测试。

验证记录:当前 head 的 Repository checks、Frontend tests、Backend tests、Native shell tests 均通过;定向资源布局模型测试与 Rust resource_layout 测试通过,git diff --check 通过。现有测试未覆盖上述合并、上限复杂度、资源更新与保存竞争、stale reclaim 竞争和无效 root 副作用。

结论:请求更改。 评审固定在 base `5479559c86e4881d51b80a3e60e4c64dac328d0e`、head `61f1306fee380d426eb9c5daeff47670f633542a`、merge-base `d29240b954c7f1acf70bd3023443f68a778e65e1`。 1. [P1] 先解决与当前目标分支的内容冲突。`git merge-tree` 与 Gitea 均确认 `apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`docs/project-memory/shared-memory/decision-log.md` 冲突。前者不能简单保留 PR 版本:base 已把 loopback 预览收口到 `LocalGamePreviewFrame`,rebase/merge 时需要同时保留该收口与本 PR 的布局能力。当前状态无法合并,也无法验证合并后的真实行为。 2. [P1] 不要让资源集合变化取消本窗口正在进行的布局写入。`useProjectResourceCanvasLayout.ts:163-170` 的初始化 effect 同时依赖 `resources`、`fallback`、`resourceSignature` 和捕获 `resources` 的 `persistLayout`;manifest、附件或 Agent 结果更新会在 `120-123` 递增 token 并清掉 saving,使旧保存回包在 `91-92` 被静默忽略,同时重新读取旧 revision。若新增资源又触发自动协调写入,它可能先赢 CAS 并落盘“旧坐标 + 新资源”,刚完成的用户拖动随后冲突但因 token 过期无人处理。无 Tauri bridge 时同一路径还会直接用 fallback 重置当前会话拖动。请拆开 project/mode 首次读取与资源协调,并串行化本窗口写入;资源数组身份不能取消已发出的保存。 3. [P1] 默认排版在合同允许的 4096 项上会冻结主线程。`resourceCanvasLayoutModel.ts:72-114` 每探测一个 slot 都线性扫描已有位置,`162-175` 又对每个新资源从头执行;同一 section 从空布局协调 N 项时接近 O(N^3)。在本 head 上,type 模式 4096 项实测约 9.5 秒,而该函数会在 render fallback 和读取回包中同步运行。请使用占用集合/行列索引或空间索引,将上限规模降到可接受复杂度,并加入接近 4096 项的性能回归测试;dependency 同层布局也需要覆盖。 4. [P1] stale 布局锁的回收会破坏“同 revision 双写最多一个成功”的核心合同。`resource_layout.rs:42-53,96-104` 只按 mtime 判 stale,未检查 token 中 owner PID 是否仍存活;而 stale 检查与 `remove_file` 也不是同一原子认领。两个竞争者可同时判定旧锁 stale:一方删除旧锁并创建新锁后,另一方仍可删除这把新锁,最终双方都进入 read-check-write。请加入活 owner 保护,并让 stale reclaim 对被检查的同一锁实例做安全认领,补同步竞争测试。 5. [P2] 无效项目路径不应先产生目录副作用。`resource_layout.rs:278-280` 在读取 `.agent/manifest.json`/确认 projectId 前先 acquire lock,而 `58-61` 会 `create_dir_all`。因此任意不存在或非项目的绝对 `projectPath` 虽最终报 manifest 错误,却已经创建 `<path>/.agent/workbench/resource-layouts/`。请先只读确认有效项目,锁内再复核身份,并补 invalid/nonexistent root 零副作用测试。 验证记录:当前 head 的 Repository checks、Frontend tests、Backend tests、Native shell tests 均通过;定向资源布局模型测试与 Rust resource_layout 测试通过,`git diff --check` 通过。现有测试未覆盖上述合并、上限复杂度、资源更新与保存竞争、stale reclaim 竞争和无效 root 副作用。
menghao marked the pull request as work in progress 2026-07-30 14:14:01 +08:00
menghao added 4 commits 2026-07-30 17:03:06 +08:00
同步目标分支 game-chat、运行预览与 Agent Runtime 最新改动
同时保留 LocalGamePreviewFrame 与资源画布双模式布局能力
合并双方项目决策记录且不包含本地环境文件
以 scope epoch 和单写者 FIFO 串行首读、拖动及资源协调写入
CAS 冲突载入权威布局并丢弃旧手动意图,资源协调有界重试
使用空间占用索引将 4096 项默认布局降至可接受复杂度
补充异步竞态、冲突、性能回归测试并同步契约文档
以 Unix flock 和 Windows 独占句柄替换 mtime stale 锁回收
持久锁文件只随句柄释放,保留安全路径、owner、硬链接与权限校验
在创建 workbench 前验证 manifest,并在锁内复核 projectId
补充活锁老化、并发 CAS、无效根零副作用测试及契约文档
完成资源画布竞态审计与最终门禁
Project CI / Repository checks (pull_request) Failing after 9s
Project CI / Backend tests (pull_request) Failing after 9s
Project CI / Frontend tests (pull_request) Failing after 20s
Project CI / Native shell tests (pull_request) Failing after 8m46s
c162e1de44
增加 expectedProjectId 身份栅栏并在写入关键阶段复核项目身份
在 revision 耗尽时失败关闭并保持乐观锁 CAS 契约
折叠同资源连续拖动并解除旧 scope 请求对新项目的阻塞
补齐多窗口并发、异步迟到响应和极端边界回归测试
修复目标分支格式、前端门禁及负载敏感测试时序
同步更新资源画布契约、决策记录和竞态风险说明
menghao added 1 commit 2026-07-30 18:34:59 +08:00
合并最新游戏创作目标分支并解决文档冲突
Project CI / Repository checks (pull_request) Successful in 1m36s
Project CI / Backend tests (pull_request) Successful in 3m25s
Project CI / Frontend tests (pull_request) Successful in 3m37s
Project CI / Native shell tests (pull_request) Failing after 9m54s
296b21521d
同步通用多智能体 Runtime 与可扩展 Provider 最新实现
完整保留资源画布与 Runtime 双方 pitfalls 长期经验
通过编码、前端类型、资源布局及 Runtime Provider 定向测试
保持本地环境文件不进入提交
menghao added 1 commit 2026-07-30 19:17:11 +08:00
修复客户端预览所有权门禁
Project CI / Frontend tests (pull_request) Successful in 3m49s
Project CI / Backend tests (pull_request) Successful in 3m56s
Project CI / Repository checks (pull_request) Successful in 55s
Project CI / Native shell tests (pull_request) Failing after 11m8s
3318be3da0
让 Native shell 检查识别共享 LocalGamePreviewFrame 的唯一物理 iframe
校验项目开发与总控对话视图分别委托共享预览组件
同步校验抽取后的 URL 安全与 sandbox 契约位置
menghao added 1 commit 2026-07-31 10:18:34 +08:00
同步开发窗口调试门禁
Project CI / Native shell tests (pull_request) Successful in 12m44s
Project CI / Repository checks (pull_request) Successful in 4m15s
Project CI / Backend tests (pull_request) Successful in 5m22s
Project CI / Frontend tests (pull_request) Successful in 5m34s
b2cc21646c
让 Native shell 检查匹配 game-chat 分流后的受保护开发窗口调用
保持 open_developer_window 函数与调用点均为编译期 debug-only
验证全部 Native shell 静态架构断言通过
menghao marked the pull request as ready for review 2026-07-31 10:31:36 +08:00
kdletters requested changes 2026-07-31 10:42:24 +08:00
Dismissed
kdletters left a comment
Member

结论:仍需请求更改。

本轮重新固定 base d4075c3423e0dd8c52deb90c9dc36130602ef1a7、head b2cc21646cf989a0acab529feaeaa18a12ac04a1,当前 merge-base 已等于 base,Gitea 显示可合并且四项 CI 全绿。上轮提出的分支冲突、资源变化取消在途保存、4096 项布局性能、stale 锁回收和无效项目路径副作用均已修复。

  1. [P1] 自动资源协调 CAS 冲突时会静默丢弃其后排队的用户拖动。useProjectResourceCanvasLayout.ts:314-333 对任何 conflict 都清空当前 scope 的全部 manual intent;但 active intent 为 resources 且资源协调仍可自动重试时,331-333 不会设置“请重新拖动”提示。可复现场景:资源同步 revision 1 在途,用户把资源 A 拖到 x=100,首写 conflict 返回权威 x=400/revision=2,自动资源重试随后成功;最终卡片回到 x=400,但 notice === ''。丢弃冲突前手动意图符合当前合同,静默丢弃不符合;只要本次 conflict 清除了任意 manual intent,就必须提示用户重新拖动,并补“resource sync 在途 + manual 排队 + conflict + retry success”的 Hook 测试。

  2. [P2] “按类型”默认布局仍未实现 PRD 冻结的资源子类型排序。PRD §5.2.4 要求按“资源子类型、媒体类型和名称”稳定排序;但 resourceCanvasLayoutModel.ts:25-31ResourceCanvasItem 没有 subtype,178-195 只比较 mediaType/label/id,而 project-development/index.tsx:317-343 又没有把 manifest.assets[].kind 传入布局模型。因此同为 image/pngui-prototypeart-spritesheet 等资源会按名称混排。请为各资源来源提供稳定 subtype(资产至少使用 asset.kind)并加入“相同 MIME、不同 subtype”测试;如果产品不再要求子类型排序,则应先同步修正 PRD 和实现状态声明。

  3. [P2] Rust u64 revision 与 TypeScript number 的 IPC 精度边界仍未闭合。shared-contracts 接受完整 u64resource_layout.rs:342-359 未限制 JS safe integer,而前端用 number 回传 expectedRevision。例如 sidecar revision 9007199254740993 到 JS 会变成 9007199254740992,之后只能持续 conflict;u64::MAX 回传时甚至超出 Rust u64 反序列化范围。现有 checked_add 和纯 Rust 的 u64::MAX 测试没有经过 JSON/Tauri 边界。请改用字符串 revision,或把持久化、读取校验和递增上限统一限制为 Number.MAX_SAFE_INTEGER,并补跨 JSON 合同测试。

验证记录:本地 AGC typecheck 通过;资源布局模型和 Hook 测试 12/12 通过;AppSurface 322/322 通过;当前 4096 项实测 type 约 23 ms、dependency 约 2 ms;git diff --check 通过。Rust 定向测试在本机最终链接阶段遇到 clang bus error,未产生源码诊断;远端 Backend tests 已成功。

结论:仍需请求更改。 本轮重新固定 base `d4075c3423e0dd8c52deb90c9dc36130602ef1a7`、head `b2cc21646cf989a0acab529feaeaa18a12ac04a1`,当前 merge-base 已等于 base,Gitea 显示可合并且四项 CI 全绿。上轮提出的分支冲突、资源变化取消在途保存、4096 项布局性能、stale 锁回收和无效项目路径副作用均已修复。 1. [P1] 自动资源协调 CAS 冲突时会静默丢弃其后排队的用户拖动。`useProjectResourceCanvasLayout.ts:314-333` 对任何 conflict 都清空当前 scope 的全部 manual intent;但 active intent 为 `resources` 且资源协调仍可自动重试时,`331-333` 不会设置“请重新拖动”提示。可复现场景:资源同步 revision 1 在途,用户把资源 A 拖到 `x=100`,首写 conflict 返回权威 `x=400/revision=2`,自动资源重试随后成功;最终卡片回到 `x=400`,但 `notice === ''`。丢弃冲突前手动意图符合当前合同,静默丢弃不符合;只要本次 conflict 清除了任意 manual intent,就必须提示用户重新拖动,并补“resource sync 在途 + manual 排队 + conflict + retry success”的 Hook 测试。 2. [P2] “按类型”默认布局仍未实现 PRD 冻结的资源子类型排序。PRD §5.2.4 要求按“资源子类型、媒体类型和名称”稳定排序;但 `resourceCanvasLayoutModel.ts:25-31` 的 `ResourceCanvasItem` 没有 subtype,`178-195` 只比较 `mediaType/label/id`,而 `project-development/index.tsx:317-343` 又没有把 `manifest.assets[].kind` 传入布局模型。因此同为 `image/png` 的 `ui-prototype`、`art-spritesheet` 等资源会按名称混排。请为各资源来源提供稳定 subtype(资产至少使用 `asset.kind`)并加入“相同 MIME、不同 subtype”测试;如果产品不再要求子类型排序,则应先同步修正 PRD 和实现状态声明。 3. [P2] Rust `u64` revision 与 TypeScript `number` 的 IPC 精度边界仍未闭合。`shared-contracts` 接受完整 `u64`,`resource_layout.rs:342-359` 未限制 JS safe integer,而前端用 `number` 回传 `expectedRevision`。例如 sidecar revision `9007199254740993` 到 JS 会变成 `9007199254740992`,之后只能持续 conflict;`u64::MAX` 回传时甚至超出 Rust `u64` 反序列化范围。现有 `checked_add` 和纯 Rust 的 `u64::MAX` 测试没有经过 JSON/Tauri 边界。请改用字符串 revision,或把持久化、读取校验和递增上限统一限制为 `Number.MAX_SAFE_INTEGER`,并补跨 JSON 合同测试。 验证记录:本地 AGC typecheck 通过;资源布局模型和 Hook 测试 12/12 通过;AppSurface 322/322 通过;当前 4096 项实测 type 约 23 ms、dependency 约 2 ms;`git diff --check` 通过。Rust 定向测试在本机最终链接阶段遇到 clang bus error,未产生源码诊断;远端 Backend tests 已成功。
menghao marked the pull request as work in progress 2026-07-31 10:59:04 +08:00
menghao added 4 commits 2026-07-31 11:37:16 +08:00
记录资源协调冲突实际清除的排队手动意图并强制提示重新拖动
补充资源重试成功后不重放旧坐标且保留提示的 Hook 时序测试
同步更新资源画布并发合同与长期竞态经验
为全部资源来源补充必填且稳定的 subtype 映射
按 subtype、媒体类型、名称和资源 ID 生成确定性类型布局
将 subtype 纳入资源协调签名并补充模型与 AppSurface 回归
同步更新资源画布产品、技术与长期决策合同
统一限制资源布局 revision 到 JavaScript 最大安全整数
在 Rust serde、Tauri 输入与前端 IPC 三层拒绝非安全 revision
补充最大值往返、上限失败、超限零副作用与不可信响应回归
同步更新资源画布合同、技术决策与竞态经验
补强资源冲突提示生命周期
Project CI / Frontend tests (pull_request) Successful in 2m56s
Project CI / Backend tests (pull_request) Successful in 3m13s
Project CI / Native shell tests (pull_request) Successful in 11m2s
Project CI / Repository checks (pull_request) Successful in 54s
39f7b06297
按当前 scope 保留已丢弃手动意图的重新拖动提示
阻止资源重试结果与通用定时器静默清除冲突提示
在新手动布局使用最新 revision 保存成功后解除提示
同步收紧资源画布产品与技术合同
menghao marked the pull request as ready for review 2026-07-31 11:50:39 +08:00
kdletters approved these changes 2026-07-31 11:57:47 +08:00
kdletters left a comment
Member

复核通过,批准合并。

评审固定在 base d4075c3423e0dd8c52deb90c9dc36130602ef1a7、head 39f7b06297cf306c14af6c46de6b5fba250de52a,merge-base 等于 base,merge-tree 干净。

上轮剩余三项均已关闭:

  • 资源协调 conflict 清除排队手动意图时,会按 scope 持续保留“请重新拖动”提示;资源重试成功/失败和通用定时器不会静默覆盖,新的手动布局成功后才解除。
  • type 默认布局已补充必填 subtype,资产使用 asset.kind,其它来源使用稳定 subtype,并纳入排序与资源签名。
  • revision 已在 Rust serde、持久读取、Tauri 输入和前端响应检查中统一限制到 Number.MAX_SAFE_INTEGER,达到上限或超限时失败关闭且不覆盖原文件。

验证记录:四项 Gitea CI 全绿;本地 AGC typecheck 通过;资源布局合同/模型/Hook 测试 18/18 通过;AppSurface 323/323 通过;shared-contracts revision 测试 2/2 通过;Tauri resource_layout 测试 11/11 通过;git diff --check 通过。未发现新的可操作阻塞项。

复核通过,批准合并。 评审固定在 base `d4075c3423e0dd8c52deb90c9dc36130602ef1a7`、head `39f7b06297cf306c14af6c46de6b5fba250de52a`,merge-base 等于 base,merge-tree 干净。 上轮剩余三项均已关闭: - 资源协调 conflict 清除排队手动意图时,会按 scope 持续保留“请重新拖动”提示;资源重试成功/失败和通用定时器不会静默覆盖,新的手动布局成功后才解除。 - type 默认布局已补充必填 subtype,资产使用 `asset.kind`,其它来源使用稳定 subtype,并纳入排序与资源签名。 - revision 已在 Rust serde、持久读取、Tauri 输入和前端响应检查中统一限制到 `Number.MAX_SAFE_INTEGER`,达到上限或超限时失败关闭且不覆盖原文件。 验证记录:四项 Gitea CI 全绿;本地 AGC typecheck 通过;资源布局合同/模型/Hook 测试 18/18 通过;AppSurface 323/323 通过;shared-contracts revision 测试 2/2 通过;Tauri resource_layout 测试 11/11 通过;`git diff --check` 通过。未发现新的可操作阻塞项。
kdletters merged commit 216407d93e into codex/ai-game-creator-app 2026-07-31 12:00:48 +08:00
kdletters deleted branch codex/canvas-layout-persistence 2026-07-31 12:00:48 +08:00
Sign in to join this conversation.