修复清单快照误判:CAS 判据面收窄为「资源 + 版本」,预览起停不再触发用户可见拒收
- projectResourceLiveUpdateModel 新增 manifestCasFingerprint,按 { assets, versions } 判定同版本号冲突
- 同 revision 且判据面一致时,仅非 CAS 账目状态(preview 运行地址/端口、任务进度、项目名)变化改为 accepted 并让内容落地,revision 不推不退
- 判据面变化仍判 revision-conflict,保留「改了被保护内容却没推 revision」的可观测信号,不吞真实缺陷
- 新增回归用例:同 revision 仅预览状态变化必须被接受;同 revision 资产变化必须仍被拒收
- 变异验证:把判据改回整份 JSON 指纹后,新用例以 expected 'revision-conflict' to be 'accepted' 失败
- 定向验证:projectResourceLiveUpdateModel 17 passed、workspaceLauncherManifestMerge 5 passed、appSurface 413 passed、npm run typecheck exit 0、npm run check:encoding 4399 files passed、git diff --check 干净
- 同步 decision-log 与 pitfalls 记录 CAS 判据面契约与本次取证
This commit is contained in:
@@ -8486,3 +8486,11 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
- 原因:如果读侧沿用栏目 ref,动作会基于最近栏目页的旧缩放/平移值计算,再写入 all;数值碰到缩放边界时用户看到的就是「按钮按了但不动」,且拖动距离也会按错误倍率换算。
|
||||
- 验证:appSurface 的多栏目「所有资源」用例连续点击两次放大;四处 ref 选择全部变异回旧实现后第二次点击断言红灯,恢复后通过。定向类型检查与 ESLint 均通过。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts`、`pitfalls.md` 本条。
|
||||
|
||||
## 2026-09-12 清单 CAS 判据面收窄为「资源 + 版本」:非 CAS 簿记状态不得再触发用户可见拒收
|
||||
|
||||
- 背景:`manifest.rs::append_agent_game_iteration_version_at` 早已写明「revision 是跨界面排序边界,前端会拒收同 revision 但 **assets 或 versions** 不同的快照」。但前端实现用 `JSON.stringify(manifest)` 整份清单当判据,判据面比契约宽,于是 `record_preview_state` / `record_command_run` / `rename_local_game_project` 这些**刻意不推 revision** 的簿记写入(预览起停、任务进度、项目名)每次落盘都会被判 `revision-conflict`,用户看到「资源清单更新被拒收(同一版本号上的清单内容不一致)」,而真正的新资源与新版本也一起被丢在门外。
|
||||
- 决策:CAS 判据面固定为 `{ assets, versions }`(`manifestCasFingerprint`)。同一 revision 下三态——判据面不同 ⇒ `revision-conflict`(真实的"改了被保护内容却没推 revision"必须继续可见,不许被吞掉);判据面相同且整份清单相同 ⇒ `duplicate`;判据面相同、只有非 CAS 账目状态不同 ⇒ `accepted`,revision 不推也不退,内容照常落地到界面。
|
||||
- 原因:预览起停不是源码变更,不能靠"给它也推一次 revision"来消除冲突——那会让运行时的验证凭证(`expected_revision` / `verified_revision` / `failed_playtest_revision`)凭空漂移,把自主构建流程拖进无谓的重新验证。正确的修法是把判据面收回到契约所说的对象,而不是让簿记写入承担源码语义。
|
||||
- 验证:`projectResourceLiveUpdateModel.test.ts` 新增「同 revision 只有 `preview` 变化必须被接受」与「同 revision 资产变化必须仍被拒收」两条;变异回整份 JSON 指纹后前者以 `expected 'revision-conflict' to be 'accepted'` 失败。定向:模型 17 passed、workspaceLauncherManifestMerge 5 passed、appSurface 413 passed、typecheck exit 0、check:encoding 4399 files passed。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src/view/project-development/projectResourceLiveUpdateModel.ts`、`apps/ai-game-creator-shell/tests/projectResourceLiveUpdateModel.test.ts`、`pitfalls.md` 本条。
|
||||
|
||||
@@ -5371,3 +5371,12 @@
|
||||
- **处理**:所有需要读取当前子画布视口的交互都按目标选择 ref:`resourceBookOpensAllResources` 时读取 `resourceBookAllViewportRef`,栏目页才读取 `resourceCanvasViewportRef`;写回仍统一经过 `setResourceCanvasViewport`,不新增持久化空间。
|
||||
- **验证**:appSurface 的「所有资源」用例新增连续两次点击放大断言;把四处 all ref 选择变异还原为旧的 `resourceCanvasViewportRef` 后,该用例在第二次点击处红,显示 transform 未变化;恢复后用例通过。
|
||||
- **关联**:`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts`、`decision-log.md` 2026-09-12 条。
|
||||
|
||||
## 2026-09-12 同一版本号上的「簿记写入」被判成 CAS 冲突:判据面比契约宽,每次预览起停都弹「资源清单更新被拒收」
|
||||
|
||||
- **现象**:真实项目「What do u wanna do kitten」(`C:\Users\dongy\Documents\Genarrative GameAgent\`,项目 revision 74)里反复出现「资源清单更新被拒收(同一版本号上的清单内容不一致),已按磁盘清单重新对齐」;连带表现是新素材/新版本进不了画布,运行模块的版本切换"看起来坏了"(`versions[]` 停在旧快照上),用户前后修过几次都没修掉。
|
||||
- **取证(日志 + 磁盘对照,不是推断)**:应用诊断日志 `%APPDATA%\world.genarrative.ai-game-creator\diagnostics\application.log` 记 `[manifest-merge] 清单快照被拒收 decision=revision-conflict source=supervisor snapshotRevision=74 heldRevision=74`(13:17 那次;同页另有十几条 `decision=duplicate`,`duplicate` 不弹提示)。对照项目目录:13:17:17 同一秒写了 `.agent/logs/preview.log` 与 `.agent/manifest.json`,而 `.agent/runtime/project-revision.json` 仍是 `revision: 74`、`updatedAt` 停在 9/11 —— 即「写了清单、没推 revision」。再对照 `App.tsx` 的 supervisor 配对读(`before`/`manifest`/`after` 三段,前后 revision 不等就重试一次),同 revision 却能通过该保护,只剩一种解释:**有写点改了清单内容却没推 revision**。
|
||||
- **原因**:`project/manifest.rs::record_preview_state` 只写 `manifest.preview`(运行地址/端口)与 `preview-playtest` 任务状态,**刻意不推 revision**——预览起停不是源码变更,推 revision 会让运行时的验证凭证(`expected_revision` / `verified_revision` / `failed_playtest_revision`)无故漂移。而权威契约(`append_agent_game_iteration_version_at` 的注释)说的是 revision 保护「**资源与版本**」。前端 `manifestFingerprint` 却拿 `JSON.stringify(manifest)` **整份清单**当 CAS 判据 ⇒ 判据面比契约宽:每次预览起停都命中"同版本号、内容不同"⇒ `revision-conflict`;更糟的是这条假冲突把快照丢掉,真正要紧的新资源与新版本也一起被挡在门外。
|
||||
- **处理**:把 CAS 判据收窄到契约面——新增 `manifestCasFingerprint`(`{ assets, versions }`),同 revision 下三态:判据面不同 → 仍 `revision-conflict`(真实的漏推 revision 必须继续可见,不许被吞掉);判据面相同且整份清单相同 → `duplicate`;判据面相同、只有非 CAS 账目状态(`preview` / 任务进度 / 项目名)不同 → `accepted`,revision **不推也不退**,让内容真正落地。同类簿记写点(`record_command_run`、`update_manifest_task_status_at`、`rename_local_game_project`、Godot 根校准)同样不推 revision,收窄判据面后不再产生用户可见拒收。
|
||||
- **验证**:新增用例 `adopts same-revision bookkeeping changes instead of rejecting them`(同 revision 只有 `preview` 从 stopped 变 running ⇒ 必须 `accepted`、`projectManifestMergeRejectionDecision` 为 null、revision 仍为 4)与对照钉子 `still fails closed when the protected assets change under the same revision`(同 revision 多出一条资产 ⇒ 必须仍 `revision-conflict`)。变异验证:把判据改回整份 JSON 指纹 → 新用例以 `expected 'revision-conflict' to be 'accepted'` 失败(正是用户看到的那条决策),恢复后转绿。定向:`projectResourceLiveUpdateModel` 17 passed、`workspaceLauncherManifestMerge` 5 passed、appSurface 413 passed、`npm run typecheck` exit 0、`npm run check:encoding` 4399 files passed、`git diff --check` 干净。
|
||||
- **关联**:`apps/ai-game-creator-shell/src/view/project-development/projectResourceLiveUpdateModel.ts`、`apps/ai-game-creator-shell/src/features/app-shell/WorkspaceLauncher.tsx`、`apps/ai-game-creator-shell/src/App.tsx`(supervisor 配对读)、`apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs`、`apps/ai-game-creator-shell/tests/projectResourceLiveUpdateModel.test.ts`。
|
||||
|
||||
Reference in New Issue
Block a user