Skip to content

feat(checkpoint): 支持文件级回退的撤销能力#648

Merged
wynxing merged 6 commits into
1024XEngineer:mainfrom
wynxing:main
May 17, 2026
Merged

feat(checkpoint): 支持文件级回退的撤销能力#648
wynxing merged 6 commits into
1024XEngineer:mainfrom
wynxing:main

Conversation

@wynxing
Copy link
Copy Markdown
Collaborator

@wynxing wynxing commented May 16, 2026

变更概述

本 PR 为文件级 baseline 回退补齐 Undo 能力。

当前在文件变更面板执行文件回退时,运行时会先为本次回退涉及的路径创建 guard checkpoint,前端则在回退完成后展示“撤销回退”入口,使用户可以将这次文件级回退恢复到回退前状态。

主要改动

Runtime / Checkpoint

  • PerEditSnapshotStore 新增按路径固化精确快照的能力 FinalizeExactForCheckpointPaths
  • baseline 模式的 checkpoint restore 在执行前会为目标路径创建 guard checkpoint
  • checkpoint_restored 事件在 baseline 模式下补充 guard_checkpoint_id
  • UndoRestoreCheckpoint 可基于该 guard checkpoint 恢复文件级回退前的状态

Web / 文件变更面板

  • 在 UI store 中新增文件回退 Undo 状态
  • 文件变更全部被 baseline 回退清空后,面板展示“撤销回退”入口
  • 用户确认后调用 undoRestore(sessionId) 执行撤销
  • 当新的 run 级文件变更到达时,自动清理过期的回退 Undo 状态
  • checkpoint_undo_restore 事件区分“文件回退的撤销”和“完整 checkpoint restore 的撤销”两类场景

解决的问题

在本次改动前,文件级 baseline 回退是单向操作:用户可以把文件恢复到 agent 修改前状态,但无法再撤销这次回退。

这会带来两个问题:

  • 文件级回退缺少可逆性,误操作成本较高
  • 文件级回退与完整 checkpoint restore 在交互体验上不一致

本 PR 让文件级回退也具备可撤销能力,并将撤销范围限制在本次回退涉及的文件路径内,避免影响无关文件。

测试覆盖

本 PR 补充了以下测试:

  • baseline restore 会返回非空的 guard_checkpoint_id
  • 文件级 baseline 回退后,Undo 只恢复本次回退涉及的路径
  • baseline restore 的路径校验与 guard 创建异常包装
  • 文件变更面板中 Undo UI、禁用态和失败提示
  • event bridge 对回退 Undo 状态的创建、清理和完成处理

close #645

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@fennoai
Copy link
Copy Markdown

fennoai Bot commented May 16, 2026

Review summary:

  1. High — web/src/components/panels/FileChangePanel.tsx around handleRollbackAll(): rollbackAll dispatches one baseline restore per rollback checkpoint, but the new undo flow only keeps a single checkpointRollbackUndo entry and undoRestore(sessionId) restores only the latest guard. When the current change list spans multiple checkpoint groups, the first N-1 restores become non-undoable even though the panel hides the entire file list and presents a single "Undo last rollback" action.

  2. Medium — web/src/utils/eventBridge.ts around the CheckpointUndoRestore handler: this branch treats any existing checkpointRollbackUndo for the session as a file-rollback undo, without checking that payload.guard_checkpoint_id matches the stored guard. If the session still has a stale rollback-undo banner and the user triggers a normal checkpoint undo elsewhere, the bridge will skip the session reload and only clear local state, leaving messages/checkpoints out of sync.

  3. Medium — internal/runtime/checkpoint_restore.go around restoreCheckpointBaseline(): after the guard checkpoint is created, a RestoreBaseline failure returns immediately without deleting the newly created guard record / per-edit snapshot. That leaves a restorable pre_restore_guard behind for a rollback that never completed, so a later UndoRestoreCheckpoint can target stale state.

Note: I attempted to submit these as a single GitHub review, but the GitHub review API rejected the inline positions with Line could not be resolved, and it also rejects review submissions with an empty comments array. Posting them here avoids dropping the findings.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 16, 2026

Codecov Report

❌ Patch coverage is 83.60656% with 10 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/checkpoint/per_edit_snapshot.go 77.14% 4 Missing and 4 partials ⚠️
internal/runtime/checkpoint_restore.go 92.30% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Collaborator Author

wynxing commented May 16, 2026

已在 c2da625 处理这轮 review,感谢指出这些边界场景。

处理内容:

  • Rollback all 保留“一次整体回退 -> 生成一个聚合 guard -> undo 撤销这次整体回退”的语义;当前实现已改为同一 rollback_checkpoint_id 下只发起一次 baseline restore,paths 聚合提交。
  • 对异常的多 rollback_checkpoint_id 同屏场景做保护,不再循环发多次 restore,避免生成多个 guard 但 UI 只展示一个 undo 入口。
  • checkpoint_undo_restore 事件现在会校验 guard_checkpoint_id 是否匹配当前 rollback undo guard;不匹配时按普通 checkpoint undo 刷新 session,避免 stale banner 误拦截。
  • baseline restore 在 guard 创建后失败时,会清理 per-edit guard 元数据,并把 guard checkpoint 标记为 broken,避免后续 undo 捡到失败遗留状态。
  • 补充了 runtime/per-edit/web/event bridge 相关回归测试,覆盖聚合 rollback、guard id mismatch、失败 guard 清理和 FinalizeExactForCheckpointPaths

验证:

  • go test ./internal/checkpoint ./internal/runtime 通过
  • npm test -- --run src/components/panels/FileChangePanel.test.tsx src/utils/eventBridge.test.ts 通过
  • npm test 全量通过

补充说明:go test ./... 仍有两个与本次改动无关的既有失败:internal/tools/bash 在当前环境找不到 powershell,以及 internal/runtime/verify 的 timeout 断言未触发。

@wynxing
Copy link
Copy Markdown
Collaborator Author

wynxing commented May 16, 2026

@xgopilot 补充测试,更改后给我的fork仓库提交更改的PR

@fennoai
Copy link
Copy Markdown

fennoai Bot commented May 16, 2026

已补充测试并推送到当前 PR 分支,提交为 a59f7404

变更

  • internal/runtime/checkpoint_flow_test.go 增加 baseline restore 的补充覆盖:session mismatchcheckpoint 无 code snapshotguard checkpoint 创建失败时清理临时 per-edit snapshot
  • web/src/test/setup.ts 增加 localStorage 的内存 fallback,确保 FileChangePanel / eventBridge 相关 Vitest 用例在当前环境可正常启动。

验证

  • go test ./internal/runtime ./internal/checkpoint
  • npm test -- --run src/components/panels/FileChangePanel.test.tsx src/utils/eventBridge.test.ts

Fork PR

View job run

@wynxing wynxing merged commit a5b9f73 into 1024XEngineer:main May 17, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Web] 接入 checkpoint 全量回退后的 Undo 取消回退能力

2 participants