## 新增字段说明 ### `CheckpointDiffResult.prev_checkpoint_id` - 所属位置: - 后端:`internal/runtime/checkpoint_restore.go` - Gateway/TUI contract:`internal/gateway/contracts.go`、`internal/tui/services/runtime_contract.go` - 前端协议:`web/src/api/protocol.ts` - 含义: - 本次 diff 的前置基线 checkpoint_id。 - 在 `scope=run` 下,它表示当前 run 开始前的权威 rollback baseline。 - 为什么需要: - `checkpoint_id` 表示 diff 的目标 checkpoint,不能表示“应该回退到哪里”。 - 前端 rollback 需要知道“本轮改动开始前”的 checkpoint,而不是“本轮结束后”的 checkpoint。 - 使用方式: - 前端文件变更项的 `checkpoint_id` 应优先绑定到 `prev_checkpoint_id`。 - 如果缺失,再使用已有文件项上的 checkpoint 或后端 warning 提示用户基线不完整。 ### `CheckpointDiffResult.workspace_drifted` - 所属位置: - 后端:`internal/runtime/checkpoint_restore.go` - Gateway/TUI contract:`internal/gateway/contracts.go`、`internal/tui/services/runtime_contract.go` - 前端协议:`web/src/api/protocol.ts` - 含义: - 当前 run 开始前是否检测到 workspace drift。 - 为什么需要: - 前端和调用方需要区分“普通 run diff”和“基线已因外部改动重锚的 run diff”。 - 这能解释为什么 `prev_checkpoint_id` 可能不是上一个普通 `end_of_turn` checkpoint。 - 使用方式: - 后端在检测到 drift 并创建 rebase checkpoint 后返回 `true`。 - 前端可配合 `warning` 展示提示,不直接用它推导文件状态。 ### `CheckpointDiffResult.warning` - 所属位置: - 后端:`internal/runtime/checkpoint_restore.go` - Gateway/TUI contract:`internal/gateway/contracts.go`、`internal/tui/services/runtime_contract.go` - 前端协议:`web/src/api/protocol.ts` - 含义: - run diff 的非致命提示信息。 - 为什么需要: - baseline 缺失、workspace drift、baseline 重锚都不一定要让 diff 请求失败。 - 但这些情况会影响用户对 diff/rollback 语义的理解,不能静默吞掉。 - 使用方式: - 后端返回可读 warning。 - 前端收到后展示 toast,例如 `Checkpoint warning: ...`。 - warning 不参与业务状态判断,只用于提示。 ### `CheckpointReasonPreRunDriftRebase` - 所属位置: - `internal/session/checkpoint_types.go` - 字段值: - `pre_run_drift_rebase` - 含义: - 表示该 checkpoint 是 run 开始前因为 workspace drift 创建的重锚基线。 - 为什么需要: - 需要把 drift rebase checkpoint 与普通 `pre_write`、`end_of_turn`、`pre_restore_guard` 区分开。 - 后续 diff、restore、调试和测试都可以按 reason 判断 checkpoint 来源。 - 使用方式: - runtime 检测到 drift 后创建该 reason 的 checkpoint。 - run diff 可将它作为 `prev_checkpoint_id`。 ### `WorkspaceCheckpointState.WorkspaceKey` - 所属位置: - `internal/checkpoint/checkpoint_manager.go` - 含义: - workspace 的稳定标识,通常由 workdir 规范化后生成。 - 为什么需要: - 同一个 session 可能切换 run,同一个 workspace 也可能跨 session 复用。 - 以 workspace 为维度保存状态,才能检测跨 session 的外部改动。 - 使用方式: - 保存和读取 workspace checkpoint state 时作为主键。 ### `WorkspaceCheckpointState.CurrentCheckpointID` - 所属位置: - `internal/checkpoint/checkpoint_manager.go` - 含义: - 当前 workspace 对应的权威 checkpoint_id。 - 为什么需要: - run start 如果没有内存 baseline,需要从持久化状态恢复当前代码基线。 - restore 成功后也需要更新它,避免下一轮 run 继续使用旧基线。 - 使用方式: - run end / restore 后写入。 - run start drift 检测时读取。 ### `WorkspaceCheckpointState.FingerprintPayload` - 所属位置: - `internal/checkpoint/checkpoint_manager.go` - 含义: - 序列化后的 workspace fingerprint。 - 为什么需要: - 需要跨进程、跨 session、重启后继续比较工作区是否 drift。 - 使用方式: - 保存时把 fingerprint JSON 化。 - 读取时反序列化后与当前扫描结果比较。 ### `WorkspaceCheckpointState.UpdatedAt` - 所属位置: - `internal/checkpoint/checkpoint_manager.go` - 含义: - workspace checkpoint state 的更新时间。 - 为什么需要: - 用于调试和后续可能的清理策略。 - 使用方式: - 保存 workspace state 时写入当前时间。 ### `RunCheckpointBaseline.SessionID` - 所属位置: - `internal/checkpoint/checkpoint_manager.go` - 含义: - baseline 所属 session。 - 为什么需要: - run_id 需要和 session_id 一起作为唯一定位条件,避免不同 session 的 run_id 冲突。 - 使用方式: - 保存和读取 run baseline 时与 `RunID` 组成联合主键。 ### `RunCheckpointBaseline.RunID` - 所属位置: - `internal/checkpoint/checkpoint_manager.go` - 含义: - baseline 所属 run。 - 为什么需要: - `scope=run` diff 必须知道当前查询对应哪一次 run。 - 使用方式: - 保存和读取 run baseline 时与 `SessionID` 组成联合主键。 ### `RunCheckpointBaseline.CheckpointID` - 所属位置: - `internal/checkpoint/checkpoint_manager.go` - 含义: - 当前 run 的权威 rollback baseline checkpoint_id。 - 为什么需要: - run 结束后,前端可能异步请求 diff;此时必须仍能恢复准确 baseline。 - 使用方式: - run start 确定 baseline 后写入。 - `checkpoint.diff(scope=run)` 查询时读取,并返回为 `prev_checkpoint_id`。 ### `RunCheckpointBaseline.Drifted` - 所属位置: - `internal/checkpoint/checkpoint_manager.go` - 含义: - 当前 run 开始前是否检测到 workspace drift。 - 为什么需要: - 异步 diff 查询时,内存里的 drift 标记可能已经清理,需要从 SQLite 恢复。 - 使用方式: - 保存 run baseline 时写入。 - run diff 查询时读取,并返回为 `workspace_drifted`。 ### `RunCheckpointBaseline.UpdatedAt` - 所属位置: - `internal/checkpoint/checkpoint_manager.go` - 含义: - run baseline 的更新时间。 - 为什么需要: - 用于调试和后续可能的清理策略。 - 使用方式: - 保存 run baseline 时写入当前时间。 ### `FileChange.status = "pending"` - 所属位置: - `web/src/stores/useUIStore.ts` - `web/src/components/panels/FileChangePanel.tsx` - 含义: - 前端已知道某个文件可能发生变化,但后端还没返回准确 added / modified / deleted。 - 为什么需要: - `ToolStart` 阶段只能从工具参数知道路径,不能可靠判断文件最终状态。 - 先显示 pending,后续由 `tool_diff`、`bash_side_effect` 或 run diff 覆盖为真实状态。 - 使用方式: - 文件写入工具开始时创建 pending 变更项。 - 后端 diff 事件到达后更新为真实状态。 ### `UIState.isRestoringCheckpoint` - 所属位置: - `web/src/stores/useUIStore.ts` - 含义: - 当前前端是否正在等待 checkpoint restore 同步完成。 - 为什么需要: - restore 期间需要禁用 accept / rollback,避免重复请求和状态竞态。 - 使用方式: - 用户确认 rollback 后置为 `true`。 - restore 事件驱动的 session reload 完成或失败后置为 `false`。 ### `ToolDiffFileEntry.kind` - 所属位置: - `web/src/api/protocol.ts` - 含义: - 后端直接声明文件变化类型:`added`、`modified` 或 `deleted`。 - 为什么需要: - 旧逻辑主要依赖 `was_new` 推断,只能区分新增和修改,无法准确覆盖删除或复杂多文件工具。 - 使用方式: - 前端优先使用 `kind` 作为文件变更状态。 - `kind` 缺失时再回退到旧的 `was_new` 推断。 ### `BashSideEffectPayload.tool_call_id` - 所属位置: - `web/src/api/protocol.ts` - 含义: - 产生副作用的 bash tool call ID。 - 为什么需要: - 用于把 bash 文件变化关联回具体工具调用。 - 使用方式: - 前端当前主要用于识别事件归属,后续可用于 UI 关联展示。 ### `BashSideEffectPayload.command` - 所属位置: - `web/src/api/protocol.ts` - 含义: - 触发文件副作用的 bash 命令文本。 - 为什么需要: - 方便调试和后续 UI 展示。 - 使用方式: - 可选字段,前端不依赖它推导文件变化。 ### `BashSideEffectPayload.changes` - 所属位置: - `web/src/api/protocol.ts` - 含义: - bash 命令造成的文件变化列表。 - 为什么需要: - bash 可以通过脚本、重定向、命令行工具间接修改文件,普通文件写入工具事件捕捉不到。 - 使用方式: - 前端遍历 changes,将文件加入文件变更面板。 - 每个 change 的 `kind` 决定 added / modified / deleted 状态。 ### `BashSideEffectPayload.preemptively_captured_paths` - 所属位置: - `web/src/api/protocol.ts` - 含义: - 执行 bash 前后端提前捕获快照的路径。 - 为什么需要: - 用于说明哪些路径已经纳入 checkpoint 捕获范围。 - 使用方式: - 当前主要作为诊断信息,前端可暂不展示。 ### `BashSideEffectPayload.uncovered_paths` - 所属位置: - `web/src/api/protocol.ts` - 含义: - bash 执行后发现变化,但没有提前捕获快照的路径。 - 为什么需要: - 用于识别 checkpoint 覆盖不足的风险路径。 - 使用方式: - 当前主要作为诊断信息,后续可用于 warning 或调试面板。 ## 为什么 当前 checkpoint 链路在以下场景中基线不稳定: 1. run 级 diff 缺少权威回退基线 - `scope=run` 只聚合当前 run 内的代码 checkpoint。 - 如果用户在两次 run 之间手动改动工作区,下一次 run 的 diff/rollback 可能仍按旧 checkpoint 推导。 - 异步 diff 查询发生在 run 结束后时,内存态 baseline 也可能丢失。 2. 空闲期 workspace drift 没有被显式记录 - runtime 没有在 run start / run end 持久化工作区 fingerprint。 - 跨 session 或重启后,同一个 workspace 的外部修改无法被识别。 - 这会导致“用户手动改动”和“agent 本轮改动”混在一起,影响文件变更面板与 rollback 语义。 3. restore 后前后端状态不同步 - 后端 restore 已改变 session 与工作区状态,但前端仍可能保留旧消息、旧 insight、旧文件变更和旧 checkpoint_id。 - restore/undo 之后下一轮文件首次变更应绑定 restored checkpoint 或 guard checkpoint,但当前事件桥接没有可靠维护这个基线。 4. 文件变更面板缺少可用的 rollback 操作 - 前端只能把文件变更标记为 accepted。 - 用户无法从具体文件变更项直接回退到对应 checkpoint。 - 生成中或 restore 进行中仍可能触发操作,存在重复调用和竞态风险。 ## 怎么做 ### 1. 持久化 workspace 与 run baseline - 新增 workspace fingerprint 持久化能力: - 保存 workspace 最新 fingerprint。 - 读取 workspace 最近 fingerprint。 - 新增 workspace checkpoint state: - 保存 workspace 当前权威 checkpoint_id。 - 保存对应 fingerprint payload。 - 新增 run checkpoint baseline: - 按 `session_id + run_id` 保存本次 run 的权威 rollback checkpoint。 - 保存 `drifted` 标记,供异步 run diff 查询使用。 - runtime 在 run start: - 扫描当前 workdir fingerprint。 - 对比上次 run end 或持久化 fingerprint。 - 如果检测到 drift,创建 `pre_run_drift_rebase` checkpoint。 - 将该 checkpoint 作为当前 run 的 baseline。 - runtime 在 run end / restore 后: - 保存最新 workspace fingerprint。 - 保存当前 workspace checkpoint state。 - 清理 run 级内存缓存,避免跨 run 污染。 ### 2. 修正 run scope diff 的 baseline 语义 - `checkpoint.diff(scope=run)` 优先使用持久化 run baseline。 - 如果没有持久化 baseline,再回退到 run 开始前最近的可用代码 checkpoint。 - 返回 `prev_checkpoint_id`、`workspace_drifted`、`warning`。 - 如果 target checkpoint 不属于当前 run,直接返回错误。 - baseline 缺失时返回 warning,不静默产出含糊 diff。 - drift rebase 场景返回明确 warning,例如 baseline 已重锚。 ### 3. 修正 restore 的文件范围和 exact state - 新增 checkpoint reason:`pre_run_drift_rebase`。 - restore 时收集目标 checkpoint 之后仍 available 的相关代码 checkpoint。 - per-edit restore 支持传入 related checkpoint IDs。 - 有 related scope 时,只恢复 target 到后续 checkpoint 之间发生净变化的路径。 - 避免 target 是全量 rebase checkpoint 时误覆盖无关文件。 - `RestoreExact` 优先使用 `ExactFileVersions`,确保 guard / rebase 恢复到 checkpoint 精确结束态。 - restore 成功后更新 workspace checkpoint state,保证下一次 run 的 drift 检测基线正确。 ### 4. 前端事件桥接维护 rollback baseline - 文件变更占位状态新增 `pending`。 - 文件首次触碰时记录 first-touch rollback checkpoint,避免后续 checkpoint 覆盖同一路径的回退基线。 - run diff 返回 `prev_checkpoint_id` 时,以它作为 rollback baseline 权威来源。 - `pre_restore_guard` 不允许覆盖最新 rollback baseline。 - restore/undo 事件只影响当前 session。 - restore/undo 后: - 清空旧文件变更。 - 重载 session 消息。 - 重置 runtime insight。 - 将下一轮 first-touch baseline 绑定到 restored checkpoint 或 guard checkpoint。 - 连续 restore 事件到达时,只应用最新一次 reload 结果。 - 支持 `bash_side_effect` 事件进入文件变更面板。 - 支持 `tool_diff.kind`,不要只依赖 `was_new` 推断状态。 ### 5. 文件变更面板增加 rollback 操作 - 对带 `checkpoint_id` 的文件变更项展示 `Rollback` 按钮。 - 点击 rollback 前展示确认弹窗。 - 确认后调用 `restoreCheckpoint({ session_id, checkpoint_id })`。 - 成功后的 UI 刷新由 restore 事件链路驱动。 - 以下状态禁用 accept / rollback: - 当前 session 正在生成。 - checkpoint restore 正在进行。 - 文件变更已 accepted / rejected。 - 文件变更缺少 `checkpoint_id`。 - restore in-flight 期间避免重复提交。 ## 做好的验收 ### 后端验收 - 两次 run 之间手动新增、修改、删除文件后,下一次 run 能检测到 workspace drift。 - 检测到 drift 时会创建 `pre_run_drift_rebase` checkpoint。 - drift rebase checkpoint 会成为当前 run 的 `prev_checkpoint_id`。 - run 结束后再次异步查询 `checkpoint.diff(scope=run)`,仍能从持久化数据读到正确 baseline。 - `checkpoint.diff(scope=run)` 在 baseline 缺失时返回 warning。 - `checkpoint.diff(scope=run)` 在 target checkpoint 不属于当前 run 时返回错误。 - restore 到 drift rebase checkpoint 时,只恢复相关变更路径,不覆盖无关用户文件。 - `RestoreExact` 能恢复到 checkpoint 的精确结束态。 - restore 成功后,workspace checkpoint state 会更新为目标 checkpoint。 ### 前端验收 - 文件写入工具开始时,文件变更面板先显示 `pending` 状态。 - tool diff / run diff 到达后,文件状态更新为真实的 added / modified / deleted。 - run diff 返回 `prev_checkpoint_id` 时,文件项的 rollback checkpoint 使用该值。 - 同一文件多次被修改时,rollback checkpoint 保持首次触碰基线,不被后续 checkpoint 覆盖。 - `pre_restore_guard` 不会覆盖下一轮 rollback baseline。 - 收到 restore 事件后: - 当前 session 消息会重载。 - runtime insight 会重置。 - 文件变更列表会清空。 - 下一轮文件变更会绑定 restored checkpoint。 - 收到 undo restore 事件后,下一轮文件变更会绑定 guard checkpoint。 - 连续 restore 事件只应用最后一次 reload 结果。 - `bash_side_effect` 能把 bash 引起的文件变化加入文件变更面板。 - 文件变更项有 `Rollback` 按钮,并且必须二次确认。 - 生成中、restore 中、缺少 checkpoint、已 review 的文件项不能触发 rollback。 - restore in-flight 时重复点击不会产生重复 restore 请求。 ### 测试验收 - Go 测试覆盖: - workspace fingerprint 持久化。 - workspace checkpoint state 持久化。 - run checkpoint baseline 持久化。 - drift rebase checkpoint 创建。 - run diff 使用持久化 baseline。 - run diff baseline 缺失 warning。 - target checkpoint 跨 run 拒绝。 - restore 不误碰无关文件。 - exact state restore。 - 前端测试覆盖: - pending 文件变更占位。 - run diff baseline 绑定。 - warning toast。 - first-touch checkpoint 不被覆盖。 - bash side-effect。 - restore/undo 后 session reload。 - restore 后下一轮 baseline 绑定。 - 连续 restore 只应用最新结果。 - FileChangePanel rollback 按钮、禁用态、二次确认、重复提交防护。 ## Related Files - `internal/checkpoint/checkpoint_manager.go` - `internal/checkpoint/per_edit_snapshot.go` - `internal/runtime/checkpoint_run_baseline.go` - `internal/runtime/checkpoint_gate.go` - `internal/runtime/checkpoint_restore.go` - `internal/runtime/run.go` - `internal/runtime/runtime.go` - `internal/session/checkpoint_types.go` - `internal/gateway/contracts.go` - `internal/cli/gateway_runtime_bridge.go` - `internal/tui/services/runtime_contract.go` - `web/src/api/protocol.ts` - `web/src/utils/eventBridge.ts` - `web/src/stores/useSessionStore.ts` - `web/src/stores/useUIStore.ts` - `web/src/components/panels/FileChangePanel.tsx`
新增字段说明
CheckpointDiffResult.prev_checkpoint_idinternal/runtime/checkpoint_restore.gointernal/gateway/contracts.go、internal/tui/services/runtime_contract.goweb/src/api/protocol.tsscope=run下,它表示当前 run 开始前的权威 rollback baseline。checkpoint_id表示 diff 的目标 checkpoint,不能表示“应该回退到哪里”。checkpoint_id应优先绑定到prev_checkpoint_id。CheckpointDiffResult.workspace_driftedinternal/runtime/checkpoint_restore.gointernal/gateway/contracts.go、internal/tui/services/runtime_contract.goweb/src/api/protocol.tsprev_checkpoint_id可能不是上一个普通end_of_turncheckpoint。true。warning展示提示,不直接用它推导文件状态。CheckpointDiffResult.warninginternal/runtime/checkpoint_restore.gointernal/gateway/contracts.go、internal/tui/services/runtime_contract.goweb/src/api/protocol.tsCheckpoint warning: ...。CheckpointReasonPreRunDriftRebaseinternal/session/checkpoint_types.gopre_run_drift_rebasepre_write、end_of_turn、pre_restore_guard区分开。prev_checkpoint_id。WorkspaceCheckpointState.WorkspaceKeyinternal/checkpoint/checkpoint_manager.goWorkspaceCheckpointState.CurrentCheckpointIDinternal/checkpoint/checkpoint_manager.goWorkspaceCheckpointState.FingerprintPayloadinternal/checkpoint/checkpoint_manager.goWorkspaceCheckpointState.UpdatedAtinternal/checkpoint/checkpoint_manager.goRunCheckpointBaseline.SessionIDinternal/checkpoint/checkpoint_manager.goRunID组成联合主键。RunCheckpointBaseline.RunIDinternal/checkpoint/checkpoint_manager.goscope=rundiff 必须知道当前查询对应哪一次 run。SessionID组成联合主键。RunCheckpointBaseline.CheckpointIDinternal/checkpoint/checkpoint_manager.gocheckpoint.diff(scope=run)查询时读取,并返回为prev_checkpoint_id。RunCheckpointBaseline.Driftedinternal/checkpoint/checkpoint_manager.goworkspace_drifted。RunCheckpointBaseline.UpdatedAtinternal/checkpoint/checkpoint_manager.goFileChange.status = "pending"web/src/stores/useUIStore.tsweb/src/components/panels/FileChangePanel.tsxToolStart阶段只能从工具参数知道路径,不能可靠判断文件最终状态。tool_diff、bash_side_effect或 run diff 覆盖为真实状态。UIState.isRestoringCheckpointweb/src/stores/useUIStore.tstrue。false。ToolDiffFileEntry.kindweb/src/api/protocol.tsadded、modified或deleted。was_new推断,只能区分新增和修改,无法准确覆盖删除或复杂多文件工具。kind作为文件变更状态。kind缺失时再回退到旧的was_new推断。BashSideEffectPayload.tool_call_idweb/src/api/protocol.tsBashSideEffectPayload.commandweb/src/api/protocol.tsBashSideEffectPayload.changesweb/src/api/protocol.tskind决定 added / modified / deleted 状态。BashSideEffectPayload.preemptively_captured_pathsweb/src/api/protocol.tsBashSideEffectPayload.uncovered_pathsweb/src/api/protocol.ts为什么
当前 checkpoint 链路在以下场景中基线不稳定:
run 级 diff 缺少权威回退基线
scope=run只聚合当前 run 内的代码 checkpoint。空闲期 workspace drift 没有被显式记录
restore 后前后端状态不同步
文件变更面板缺少可用的 rollback 操作
怎么做
1. 持久化 workspace 与 run baseline
session_id + run_id保存本次 run 的权威 rollback checkpoint。drifted标记,供异步 run diff 查询使用。pre_run_drift_rebasecheckpoint。2. 修正 run scope diff 的 baseline 语义
checkpoint.diff(scope=run)优先使用持久化 run baseline。prev_checkpoint_id、workspace_drifted、warning。3. 修正 restore 的文件范围和 exact state
pre_run_drift_rebase。RestoreExact优先使用ExactFileVersions,确保 guard / rebase 恢复到 checkpoint 精确结束态。4. 前端事件桥接维护 rollback baseline
pending。prev_checkpoint_id时,以它作为 rollback baseline 权威来源。pre_restore_guard不允许覆盖最新 rollback baseline。bash_side_effect事件进入文件变更面板。tool_diff.kind,不要只依赖was_new推断状态。5. 文件变更面板增加 rollback 操作
checkpoint_id的文件变更项展示Rollback按钮。restoreCheckpoint({ session_id, checkpoint_id })。checkpoint_id。做好的验收
后端验收
pre_run_drift_rebasecheckpoint。prev_checkpoint_id。checkpoint.diff(scope=run),仍能从持久化数据读到正确 baseline。checkpoint.diff(scope=run)在 baseline 缺失时返回 warning。checkpoint.diff(scope=run)在 target checkpoint 不属于当前 run 时返回错误。RestoreExact能恢复到 checkpoint 的精确结束态。前端验收
pending状态。prev_checkpoint_id时,文件项的 rollback checkpoint 使用该值。pre_restore_guard不会覆盖下一轮 rollback baseline。bash_side_effect能把 bash 引起的文件变化加入文件变更面板。Rollback按钮,并且必须二次确认。测试验收
Related Files
internal/checkpoint/checkpoint_manager.gointernal/checkpoint/per_edit_snapshot.gointernal/runtime/checkpoint_run_baseline.gointernal/runtime/checkpoint_gate.gointernal/runtime/checkpoint_restore.gointernal/runtime/run.gointernal/runtime/runtime.gointernal/session/checkpoint_types.gointernal/gateway/contracts.gointernal/cli/gateway_runtime_bridge.gointernal/tui/services/runtime_contract.goweb/src/api/protocol.tsweb/src/utils/eventBridge.tsweb/src/stores/useSessionStore.tsweb/src/stores/useUIStore.tsweb/src/components/panels/FileChangePanel.tsx