legacy 内联账本一次性迁入本机
账本移出布局后,hydrate 仍认布局里的 legacy 内联快照,但没有任何路径把它 写进本机账本;而序列化会在下一次保存时把内联快照剥成 perfectPixelOperationId 标记。先前提交声称「滚动部署期间的在途操作不会被一次性判死」只对了一半: 第一次 hydrate 活下来,第二次就变成 failed + invalid,永久失去 exact retry 的 identity。 在 applyProjectSnapshot 读账本、splitCanvasLayoutItems 之后补一次性迁移。 三个条件都必要:只补写本机缺失的(本机那份可能刚在 pre-POST flush 之后被 重新锚定过,比布局里的新);只补写未收口的(收口态本就不需要账本)。 未采纳「删除后立即 flush 压缩竞态窗口」:核查发现删除已经触发既有的 450ms 防抖自动保存,该改动只能在由服务端处理耗时(数秒)主导的竞态里省下 450 毫秒, 代价是让高频操作绕过防抖。 未采纳「重做时弹确认框」:完美像素免费,最坏是素材库多一份;在常用路径上加 确认属于制造摩擦,且账本只能按 sourceResourceId 匹配同源,纯本地图层匹配不到, 覆盖不全的提醒比没有提醒更容易让人误以为安全。 Undo 复活占位在当前会话内不再对账(刷新即自愈)记为已知限制,连同根因与四种 修法各自的硬伤一并写入专题文档与决策日志。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -6428,3 +6428,14 @@
|
||||
- 保留为已知缺口而非静默修正:「普通按钮不得创建第二个 operation」原文是绝对断言,但该保证只由 `existingOperation` 闸提供,而它只扫描内存 dialog 列表;占位可删之后,删掉再从源图发起会产生第二个 identity。文档改为如实描述现状并标注缺口与闭合方向(让本机账本参与防重),代码侧不在本次范围。文档的职责是描述系统实际行为,写一条做不到的保证比留一个标注清楚的缺口更糟。
|
||||
- 影响范围:仅文档。不改代码、不改测试。
|
||||
- 验证方式:`npm run check:encoding`;核对文档中不再残留「布局保存成功确认」「strict revision ACK」「从快照写入起算」等已推翻表述。
|
||||
|
||||
## 2026-08-05 legacy 内联账本一次性迁入本机;Undo 复活占位记为已知限制
|
||||
|
||||
- 背景:账本移出布局后,`hydrateCanvasGenerationDialog` 仍然认布局里的 legacy 内联快照,但**没有任何路径把它写进本机账本**;而 `serializeDialogReferences` 会在下一次保存时把内联快照剥成 `perfectPixelOperationId` 标记。先前提交声称「滚动部署期间的在途操作不会被一次性判死」只对了一半:**第一次 hydrate 活下来,第二次就变成 `failed + invalid`**,永久失去 exact retry 的 identity。
|
||||
- 决策:在 `applyProjectSnapshot` 读账本、`splitCanvasLayoutItems` 之后补一次性迁移——把带内联账本、本机却读不到、且**仍未收口**(`generating` / `pending-confirmation`)的 operation 写进本机账本。三个条件都必要:只补写缺失的(本机那份可能刚在 pre-POST flush 之后被重新锚定过,比布局里的新,不能覆盖);只补写未收口的(收口态本就不需要账本,迁移只会造出立刻被裁剪的垃圾条目)。影响范围一次性且有界,仅限部署那一刻仍在途的历史操作。
|
||||
- 未采纳:「删除占位后立即 flush 布局,压缩『被放弃的 operation 仍可能往画布插入图层』的竞态窗口」。核查后发现删除**已经**触发既有的 450ms 防抖自动保存(布局自动保存 effect 的依赖里就有 `canvasGenerationDialogs`),所以该改动只能在一个由服务端处理耗时(数秒)主导的竞态里省下 450 毫秒,代价却是让一个高频操作绕过防抖、增加 PATCH 量。收益与代价不成比例,不做。
|
||||
- 未采纳:「用户删除未收口占位后从源图重做时弹确认框」。完美像素免费,重复的最坏后果是素材库多一份;为此在常用路径上加一次确认属于给用户制造摩擦。另外账本里能用来匹配同源的只有 `request.sourceResourceId`,纯本地图层根本匹配不到——一个覆盖不全的提醒比没有提醒更容易让人误以为安全。
|
||||
- 记为已知限制而非缺陷:删除仍在处理中的占位、待原请求收口后再 `Ctrl+Z` 撤销删除,复活的占位在**当前会话内**不再被对账,会一直显示处理中;刷新即自愈,用户也可以再删一次。根因是 `observedPerfectPixelRecoveryKeysRef` 同时承担「并发保护」和「本会话已驱动过」两种语义。已推演的四种修法各有硬伤:删除瞬间剪 key 会被同一轮的 claim 检查重新标记;改成「重新出现时剪」会被 `applyProjectSnapshot` 的整批替换误触发;由删除路径显式清观察记录需要向四个删除入口铺跨 hook 通路;不把未收口 operation 放进可撤销历史则直接砍掉「误删可撤销」。为一个刷新即愈的限制付上述任一代价都不划算,留到重构该记账时一并解决。
|
||||
- 影响范围:`useImageCanvasProjectPersistence.ts` 的 `applyProjectSnapshot`。不修改服务端、SpacetimeDB schema 或对外契约。
|
||||
- 验证方式:新增「legacy 内联账本在加载后被迁入本机,且剥离内联快照后仍能凭本机账本往返回有效的 `pending-confirmation` 占位」用例,已实证:回退迁移后报 `expected +0 to be 1`。运行 `npx vitest run src/components/image-editor src/components/platform-entry`、`npm run typecheck`、`npm run lint:eslint`、`npm run check:encoding`。
|
||||
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`(已同步 legacy 迁移要求与 Undo 已知限制)。
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user