Skip to content

pref(checkpoint):修复 Checkpoint 恢复与 Run 级 Diff 完整性#572

Merged
phantom5099 merged 4 commits into1024XEngineer:mainfrom
phantom5099:file-checkpoint
May 8, 2026
Merged

pref(checkpoint):修复 Checkpoint 恢复与 Run 级 Diff 完整性#572
phantom5099 merged 4 commits into1024XEngineer:mainfrom
phantom5099:file-checkpoint

Conversation

@phantom5099
Copy link
Copy Markdown
Collaborator

@phantom5099 phantom5099 commented May 7, 2026

问题

  1. 恢复后新文件残留

Agent 在运行过程中创建了新文件,restore 到更早的 checkpoint时,新文件未被删除。根因三层:

  • Finalize 只写 pending 子集:当某 turn 无文件修改时,Finalize返回 (false, nil),生成 CodeCheckpointRef="" 的 session-only checkpoint。restoreCheckpointCore 遇到非 per-edit ref时整段跳过代码恢复路径。
  • Restore 的 hashSet 不完整:即使进入 Restore,无 guard 时hashSet 仅含目标 checkpoint 自身FileVersions,不含后续创建的文件 → for 循环遍历不到 → 残留。
  • Finalize 与 FinalizePending 语义混淆:FinalizePending 仅写pending(guard 专用),保证 guard 不包含多轮前的旧 pre-write内容;Finalize 写全量快照,保证每个 checkpoint 都有完整CodeCheckpointRef。
  1. Restore 语义模糊

旧 Restore 仅接受 targetID 单参数,用 v_next单快照推断目标状态。对于post-delete、删除后重建、目录操作等场景需要复杂的启发式推断。

  1. move / delete_dir 操作源路径残留

filesystem_move_file执行后源文件未标记删除,filesystem_remove_dir执行后目录内嵌套文件未标记删除。restore时这些已删除路径会被错误地"复活"。

  1. Run 级 diff 基线定位修正

scope=run 聚合 diff 对每个文件取 run 内首次被触碰前的版本作为baseline,取 run 内最后版本号的下一版本作为 after侧(contentAfterLastVersionLocked)。但最初 ListCheckpointOpts缺失 RunID 过滤,且 restoreCheckpointCore 中的markRestoredIDs 未排除 guard checkpoint(导致 undo信息丢失)。


方案

  1. Finalize 全量快照 + FinalizePending 保护 Guard
  • Finalize (per_edit_snapshot.go:229):遍历 pathToVersions全量索引,为每个 hash 取最新非 IsPostDelete版本号,生成完整快照。这样每个 checkpoint 都有完整CodeCheckpointRef,restore 路径始终生效。
  • FinalizePending (per_edit_snapshot.go:282):仅写当前 turn 的pending 子集,专用于 pre-restore guard checkpoint。避免 guard包含多轮前的旧 pre-write 内容,导致 undo 时写错状态。
  1. 双快照对比恢复

Restore(targetID, guardID) 改为对比两个明确的状态快照:

  • to 状态:目标 checkpoint时刻的文件状态(contentAtCheckpointLocked + v_next语义,fallbackIfMissing=false)
  • from 状态:guard checkpoint 时刻(或当前磁盘)的文件状态
  • 一致则跳过;目标不存在则 os.RemoveAll;目标为目录则MkdirAll;否则写回内容

Guard checkpoint 恢复走 RestoreExact 单独路径:直接按 guard记录的版本号精确写回,不做双向对比。

  1. 无 guard 时合并全量索引

Restore 中 hasGuard=false 时,hashSet 额外并 pathToVersions中所有已知文件。后续创建的文件的 hash 不在 target FileVersions中 → contentAtCheckpointLocked(hash, targetCP, false) 返回toExists=false → 触发 os.RemoveAll。

  1. IsPostDelete 标记 + CapturePostDelete
  • CapturePostDelete (per_edit_snapshot.go:178):写入IsPostDelete=true, Existed=false 的 .meta(不写.bin),标记该路径已被 delete/move 移除。
  • Finalize 构建全量快照时跳过 IsPostDelete=true的标记版本,确保 checkpoint 记录文件内容的最近版本号。
  • toolexec.go 中 filesystem_move_file
    对源路径、filesystem_remove_dir
    对目录及其嵌套文件、filesystem_delete_file
    对目标路径,均在执行成功后调用 CapturePostDelete。
  1. RunDiff + RunID 过滤 + markRestoredIDs 修正
  • contentAfterLastVersionLocked(per_edit_snapshot.go:927):取最后版本号的 v_next 作为 after侧;无后续版本时回退到 readWorkdirContent(此时 run结束后无触碰,当前磁盘即最终状态)。
  • RunAggregateDiff(per_edit_snapshot.go:486):对每个文件取最小版本为 beforebaselinecontentAfterLastVersionLocked 为 after 侧。
  • ListCheckpointOpts.RunID(checkpoint_manager.go:40):新增字段,runDiff 按 run_id精确过滤,替代原 Limit:500 粗暴拉取。
  • restoreCheckpointCore 中 markRestoredIDs 排除CheckpointReasonGuard 类型的 checkpoint,保证 undo链路不被截断。
  1. EventRunDiffSummary 事件

新增 EventRunDiffSummary 事件 (events.go:435) 和RunDiffSummaryPayload 类型,在 run 结束时由 run.go 触发,携带FromCheckpointID、ToCheckpointID、Diff、ChangedFiles。


修改范围

文件: internal/checkpoint/per_edit_snapshot.go
变更: Finalize 全量快照;FinalizePending增量快照;Restore(targetID, guardID) 双快照对比;RestoreExactguard 专用恢复;CapturePostDelete post-delete标记;RunAggregateDiff +contentAfterLastVersionLocked;contentAtCheckpointLocked增加fallbackIfMissing 参数

文件: internal/checkpoint/per_edit_snapshot_test.go
变更: 16 处 Restore 调用更新为三参数签名;新增TestRestore_Directory*、TestRestoreExact、TestRunAggregateDi
ff*、TestCheckpointDiffAfterDeleteAndRecreate、TestCheckpointDiffWithPostDeleteGuards*等

文件: internal/checkpoint/checkpoint_manager.go
变更: ListCheckpointOpts 增加 RunID 字段及 SQL 过滤

文件: internal/runtime/checkpoint_restore.go
变更: 提取 restoreCheckpointCore 核心逻辑;guard 使用FinalizePending;Restore 双参数调用;runDiff实现;markRestoredIDs 排除 guard checkpoint

文件: internal/runtime/checkpoint_gate.go
变更: lastEndOfTurnCheckpointID记录;findPreviousEndOfTurnCheckpoint 辅助方法

文件: internal/runtime/toolexec.go
变更: CapturePostDelete:move 源路径、remove_dir目录+嵌套文件、delete_file 目标路径;bash side effect路径记录;removeDirNestedPaths 集合传递给 post-delete

文件: internal/runtime/events.go
变更: 新增 EventRunDiffSummary 事件和 RunDiffSummaryPayload

文件: internal/runtime/run.go
变更: run 结束时触发 EventRunDiffSummary

文件: internal/repository/fingerprint.go
变更: ScanWorkdir / DiffFingerprints 从 checkpoint 包迁入,供
bash 指纹扫描重用

文件: internal/gateway/contracts.go
变更: CheckpointDiffInput 增加 Scope / RunID 字段


TUI/GUI 接入状态

已接入

  • GUI (Web) CheckpointInlineMark:展示 checkpoint,支持 Restore / Undo Restore,通过 gatewayAPI.checkpointDiff 拉取diff。
  • Runtime insight store:消费 CheckpointCreated /CheckpointRestored / CheckpointUndoRestore 事件,更新checkpointEvents 和 toast。
  • Runtime 侧:EventRunDiffSummary 已在 run 结束时发送。

待接入

  • Web CheckpointDiffParams(web/src/api/protocol.ts:248):目前仅含 session_id +checkpoint_id?,需扩展 scope 和 run_id 以接入 run 级聚合diff。
  • Web eventBridge:EventRunDiffSummary已定义但未在事件桥中消费,接入后可在消息流中内联展示 run 级diff 卡片。
  • TUI:checkpoint 相关事件经 remote_runtime_adapter 转发到 TUI侧,但 TUI 暂无 checkpoint 列表、diff 展示、undo 操作的 UI实现。

@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[bot]

This comment was marked as outdated.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 7, 2026

@phantom5099
Copy link
Copy Markdown
Collaborator Author

/review 检查一遍还有没有存在checkpoints创建出错、run级diff存在遗漏或是参杂外部修改、代码回退出现遗漏等问题

Copy link
Copy Markdown

@fennoai fennoai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

发现 3 个需要处理的问题,集中在 scope=run 的结果稳定性和 restore 的边界场景。

Comment thread internal/checkpoint/per_edit_snapshot.go
Comment thread internal/checkpoint/per_edit_snapshot.go
Comment thread internal/runtime/checkpoint_restore.go
@phantom5099
Copy link
Copy Markdown
Collaborator Author

/code 提高测试覆盖率,检查一遍还有没有存在checkpoints创建出错、run级diff存在遗漏或是参杂外部修改、代码回退出现遗漏等问题,过程中如果发现有不符合预期行为的地方就输出总结告诉我,不要硬给漏洞补测试掩盖错误

@fennoai

This comment was marked as resolved.

@phantom5099 phantom5099 merged commit 3a19418 into 1024XEngineer:main May 8, 2026
2 of 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.

1 participant