Skip to content

fix: optimistic rewind + checkpoint boundary corrections#4269

Closed
CVEngineer66 wants to merge 3 commits into
esengine:main-v2from
CVEngineer66:fix/rewind-checkpoint-clean
Closed

fix: optimistic rewind + checkpoint boundary corrections#4269
CVEngineer66 wants to merge 3 commits into
esengine:main-v2from
CVEngineer66:fix/rewind-checkpoint-clean

Conversation

@CVEngineer66

Copy link
Copy Markdown
Contributor

概述 / Summary

修复回溯系统的三个边界 bug,并实现乐观回溯:点击回溯后前端立即截断显示并回填输入框,真正的 Go Rewind 延迟到用户发送新消息时才执行。撤回仅需恢复 fullItems,无需任何 Go 调用。

Fixes three boundary bugs in the rewind/checkpoint system and implements optimistic rewind: clicking rewind immediately truncates the UI and refills the composer; the real Go Rewind is deferred until the user sends a new message. Undo restores fullItems with zero Go calls.


改动 / What changed

1. 乐观回溯 / Optimistic rewind

  • 非 fork 非 code 的回滚:前端立即截断 displayItems,回填 composer 原消息,显示 undo banner
  • 真正的 Go Rewind 延迟到用户发送新消息时(commitThenSend 包裹 send)
  • 撤回:setRewindState(null),恢复 fullItems,零 Go 调用
  • scope === 'code' 仅回滚文件,不截断对话
  • rewind 失败时恢复 fullItems 而非静默忽略

2. CheckpointHasBoundary 修复

压缩后 cpBound[turn] key 仍在但 boundary 值过期。之前只检查 key 存在,导致 CanConversation=true 但点下去报错。现在同时验证 boundary <= len(Messages)。

3. List() cur.Paths 修复

流式输出期间 cur.Files 不断增长,前向 CanCode 传播把所有前置 turn 的按钮全部错误 enabled。cur.Paths 置空后 cur 仍在列表中显示(最后一条消息可回滚对话),但文件不参与传播。

4. RestoreCode notice 优化

只在文件确实变更时才发 notice,避免 '0 file(s) restored, 0 removed' 误导。


Files

  • desktop/frontend/src/App.tsx — 乐观回溯 + undo banner + code-only 跳过
  • internal/checkpoint/checkpoint.go — List() cur.Paths nil
  • internal/control/controller.go — CheckpointHasBoundary + RestoreCode notice

wufengfan added 3 commits June 13, 2026 13:12
- UndoRewindBanner: shows above composer after non-fork rewind, displaying
  turns rolled back and files changed, with two-click confirm undo button
- Silent fork before rewind: creates a backup tab that undo switches to,
  fully restoring the pre-rewind session
- rewindSignal prop: Transcript scrolls to the last user message after
  rewind so the user sees exactly where they rewound to
- CSS: .undo-rewind styles matching the composer area
- Locales: undoRewind keys in en/zh/zh-TW
CheckpointHasBoundary previously only checked whether the cpBound[turn]
key existed.  After compaction the key remains but its value is stale
(boundary > len(Messages)), so the UI showed the rewind button as
enabled despite the backend refusing the operation with 'conversation
rewind unavailable for turn N: the conversation was compacted past
this point'.

Now CheckpointHasBoundary also validates boundary <= len(Messages),
so compacted turns get CanConversation=false and the frontend button
is correctly disabled.
Frontend:
- Optimistic rewind: immediate UI truncation, undo via restore,
  deferred Go Rewind on actual send.  Composer refill with
  rewound-to prompt.  Code-only rewind skips truncation.
- displayItems memo: switches between full and truncated items
  based on rewindState.
- send wrapper: commits any pending rewind before sending.

Go backend:
- CheckpointHasBoundary: also validate boundary <= len(Messages),
  so compacted turns correctly show CanConversation=false.
- List(): nil cur.Paths so in-progress file snapshots don't
  pollute CanCode backward-propagation.
- RestoreCode: only emit notice when files actually changed
  (skip confusing '0 file(s) restored, 0 removed').
@github-actions github-actions Bot added v2 Go rewrite (1.x) — main-v2 branch, active development desktop Wails desktop app (desktop/**) agent Core agent loop (internal/agent, internal/control) labels Jun 13, 2026
@CVEngineer66 CVEngineer66 force-pushed the fix/rewind-checkpoint-clean branch 2 times, most recently from 673c581 to afa09d3 Compare June 13, 2026 07:41
@esengine

Copy link
Copy Markdown
Owner

Thanks @CVEngineer66 — really nice work. Anchoring CheckpointHasBoundary to the same boundary <= len(Messages) guard that Rewind itself uses is exactly right, and the optimistic-rewind flow is a great UX call.

Landed in #4272 with your four commits preserved, plus a small follow-on pass: on a deferred-rewind failure I leave the optimistic state cleared (so the transcript matches the still-intact Go conversation) rather than re-truncating; deferred the scope=both dock refresh until the rewind actually commits; clear the refilled composer on undo; and sorted the cumulative Files list (Go map order is nondeterministic). All credited to you via Closes #4269.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Core agent loop (internal/agent, internal/control) desktop Wails desktop app (desktop/**) v2 Go rewrite (1.x) — main-v2 branch, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants