Skip to content

fix(agent-runtime): persist evidence ledger - #2426

Merged
limityan merged 4 commits into
GCWing:mainfrom
zvzuola:codex/persist-evidence-ledger
Aug 24, 2026
Merged

fix(agent-runtime): persist evidence ledger#2426
limityan merged 4 commits into
GCWing:mainfrom
zvzuola:codex/persist-evidence-ledger

Conversation

@zvzuola

@zvzuola zvzuola commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Persist EvidenceLedger events across session unloads, restarts, and restores to prevent long-running tasks from losing checkpoints, compression evidence, and partial subagent results.

The ledger is stored as a versioned evidence-ledger.json sidecar under the resolved session directory.

Type and Areas

Type: Bug fix / reliability

Areas: Rust core, Agent Runtime, session persistence, remote workspace

Motivation / Impact

Previously, EvidenceLedger existed only in process memory. When a session was unloaded or the host restarted, the ledger was lost. This could cause long-running tasks to lose important execution evidence and drift away from their original objective.

This change:

  • Persists durable session evidence to a versioned JSON sidecar.
  • Uses session-level serialization, cross-process file locking, and strict atomic replacement.
  • Makes event appends idempotent by event_id.
  • Rejects conflicting payloads for duplicate event IDs.
  • Loads and validates the ledger before publishing restored runtime state.
  • Preserves the original sidecar when the data is corrupt, from a newer schema, or belongs to another session.
  • Keeps legacy payloads readable through additive defaults and unknown-enum fallbacks.
  • Publishes evidence to in-memory state only after successful persistence.
  • Makes checkpoint persistence fail closed for mutating Bash, file, and Git tools.
  • Persists partial subagent timeout evidence while retaining already-produced partial results if persistence fails.
  • Uses the resolved remote session mirror for remote workspaces instead of reusing controller-side paths.

No new user-facing strings, locale changes, or UI changes are required.

Verification

  • cargo check --locked -p bitfun-agent-runtime --no-default-features --features agent-runtime — passed.
  • cargo test --locked -p bitfun-agent-runtime --no-default-features --features agent-runtime --lib evidence_ledger — 11 passed.
  • cargo test --locked -p bitfun-core --no-default-features --features agent-runtime --lib evidence_ledger — 4 passed.
  • cargo test --locked -p bitfun-core --no-default-features --features agent-runtime --lib concurrent_evidence_appends_keep_disk_and_memory_complete — passed.
  • cargo test --locked -p bitfun-core --no-default-features --features agent-runtime,remote-workspace --lib remote_workspace_evidence_uses_the_resolved_session_mirror — passed.
  • cargo check --locked -p bitfun-core --no-default-features --features product-full — passed.
  • pnpm run check:core-boundaries — passed.
  • pnpm run fmt:rs — passed.
  • git diff --check — passed.

The remote workspace scenario was exercised. Remote control, Peer Device Mode, and Detached Dispatch protocols were not changed by this PR and were not modified in the tests.

Reviewer Notes

  • The persistence format is currently schema version 1.
  • The sidecar is created only for durable sessions; transient sessions retain their existing in-memory behavior.
  • Restore is fail-closed for invalid or incompatible ledger data and does not delete or overwrite the original sidecar.
  • Concurrent appends are serialized in-process and protected across processes by the JSON file lock.
  • Existing repository feature-conditional warnings remain, but all targeted tests and checks pass.

Checklist

  • This PR is focused and does not include secrets, temporary prompts, generated scratch files, or unrelated artifacts.
  • Relevant verification is recorded above.
  • User-facing strings, docs, and locales are updated where applicable.

@limityan limityan left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

需要修改:回退会话后,已撤销的证据会在重启后重新出现

发生场景

  1. 某个会话 turn 产生了文件修改前的 checkpoint、失败命令,或 subagent 的部分结果。
  2. 用户把会话回退到这个 turn 之前,表示这些后续执行已经被撤销。
  3. 用户关闭并重新打开会话,或应用重启后恢复该会话。

当前行为

恢复代码会正确隐藏回退点之后的对话 turn,但随后仍会从 evidence-ledger.json 加载全部历史证据。永久提交回退时,代码也只删除对话 turn、上下文快照和压缩产物,没有同步删除被回退 turn 对应的 ledger 记录。

造成的后果

重启后,已经被用户撤销的文件、失败命令和 subagent 结果会再次进入上下文压缩与 Deep Review。系统会把旧工作误认为当前事实,可能让 Agent 继续处理已经撤销的文件或问题。换句话说:界面中的会话已经回退,但 Agent 使用的证据没有一起回退。

修复要求

  • staged revert 和永久回退都应按仍然保留的 turn ID,同步裁剪磁盘 sidecar 与内存 ledger。
  • 恢复会话时,只应发布当前可见 turn 对应的证据。
  • 增加回归测试:先产生 evidence,再 rollback,随后 unload/restore,确认 compression contract 不再包含被撤销 turn 的证据。

该问题会破坏本 PR 要保证的恢复一致性,因此修复前建议不要合并。

@limityan limityan left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

仍需修改:Undo 后再 Redo 会永久丢失证据

这次修改已经处理了永久回退后的旧证据问题,但 staged Undo 是可撤销的,目前在这个阶段就把磁盘证据删除了。

发生场景

  1. 会话中有多个 turn,并记录了 checkpoint、失败命令或 subagent 的部分结果。
  2. 用户执行 Undo,临时隐藏末尾 turn。
  3. 用户执行 Redo,希望恢复刚才隐藏的内容。

当前行为

apply_staged_revert_context_locked 在 staged Undo 阶段调用 ledger retention,并重写 evidence-ledger.json,只保留当前可见 turn 的证据。

Redo 随后能够恢复对话 turn、上下文和工作区,但已从 sidecar 删除的 evidence 无法重建。新增测试也只验证 Undo 后隐藏证据被删除,没有覆盖 Redo。

造成的后果

用户看到的会话和工作区已经恢复,Agent 使用的 checkpoint、失败命令及 subagent 结果却永久缺失。后续上下文压缩和 Deep Review 会基于不完整的历史继续执行。如果应用在 Clearing 阶段退出,恢复后也会保留这个不一致状态。

修复要求

  • staged Undo 和 staged restore 只过滤内存中的可见 evidence,保留完整 sidecar 以支持 Redo。
  • 用户提交永久回退,或开始新任务使 staged suffix 正式失效时,再裁剪磁盘 sidecar。
  • 增加 Undo → Redo、连续 Undo → 连续 Redo,以及 Clearing 阶段恢复的回归测试。

该问题会导致可恢复历史的数据丢失,修复前建议不要合并。

@limityan limityan left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

已按完整的 evidence sidecar 生命周期复核当前 head 3215d1b。上一轮 staged Undo/Redo 丢失 evidence 的问题已经修复;当前还需要处理下面两条。

[P1] Fork 后会丢失已继承历史的 evidence

场景

用户从一个已经运行较久的 Session 创建 Fork。源 Session 中已有 checkpoint、工具执行证据或 partial subagent 结果。

当前行为

session_branch.rs 会把历史 turns、上下文快照、压缩记录、prompt cache 和 metadata 复制到新 Session,但没有复制 evidence-ledger.json。Desktop、CLI 和 App Server 的 Fork 功能都会经过这条路径。

影响

新 Session 看起来继承了完整历史,实际缺少这些历史对应的验证证据。后续发生上下文压缩或 Deep Review 时,系统无法看到 Fork 之前已经产生的 checkpoint、失败记录和 partial result,长任务可能据此作出错误判断。

建议

PersistenceManager::branch_session 的同一事务中:

  1. 读取源 Session 的 ledger;
  2. 只保留本次实际复制的 turns 对应的事件;
  3. 将事件的 session_id 更新为目标 Session;
  4. 在目标 Session writer lease 下原子写入新的 sidecar;
  5. 增加通过正式 Fork port 创建分支、重新加载目标 Session,并验证 compression/Deep Review contract 的测试。

[P2] 恢复时过滤掉的旧 evidence,会在下一次工具写入后重新出现

场景

新版已经生成 evidence-ledger.json,随后旧版本修改或回退了 Session 历史。旧版本不了解这个 sidecar,因此被删除 turn 的 evidence 仍留在文件中。用户再次使用当前版本打开该 Session,并继续运行一个会产生 checkpoint 的工具。

当前行为

恢复 Session 时,session_manager.rs 会按现存 turn 过滤 evidence,但只过滤内存中的结果,sidecar 仍保留完整内容。下一次 append_evidence_event 会读取完整 sidecar、追加新事件,再用全部事件覆盖内存,刚刚被过滤掉的旧 evidence 因而重新出现。

影响

已撤销或已不存在 turn 的 checkpoint、partial subagent 结果会再次进入 compression contract 和 Deep Review,恢复阶段的过滤失去作用。

建议

普通恢复场景下,将 sidecar 收敛到现存 turns;若存在 staged Undo marker,则继续保留磁盘上的隐藏 suffix 供 Redo 使用。每次 append 后发布内存状态前,也需要按当前可见 turns 投影,避免不一致数据重新进入运行时。

除以上两条外,本次已覆盖写入、恢复、Fork、Undo/Redo、永久回退、崩溃阶段恢复、并发写入、跨进程锁和远程镜像,未发现其他需要报告的问题。

…ar on restore

P1: Session branching now copies evidence ledger events for inherited
turns, rewriting session_id to the fork target. Previously the fork
lost all inherited evidence (checkpoints, failed commands, partial
subagent results).

P2: Session restore now converges the evidence sidecar to surviving
turns when there is no staged undo marker, preventing stale events
from re-entering memory on the next evidence append. The append path
also projects persisted events to the session's visible turns before
publishing to memory.

Added regression tests for both fixes.

@limityan limityan left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

复审通过。

上一轮提出的两条问题已经完整修复:

  • Session Fork 会按实际继承的 turns 复制 evidence,重写目标 session_id,并在目标 Session 的 writer lease 下原子写入;失败时仍会清理整个目标 Session。
  • 普通 restore 会将 sidecar 收敛到现存 turns,append 发布到内存前也会按当前可见 turns 过滤;staged Undo/Redo、连续 Undo/Redo 和 Clearing 恢复仍会保留并恢复隐藏 evidence。

本次复审未发现新的可操作问题。Core evidence、Agent Runtime ledger、staged 状态机、branch、Clearing 恢复和并发追加专项测试均通过,独立隔离审查结论一致。

当前 review 对应 head:a2d029b66e3fffe123de31285012f1fdd91012dd。提交 review 时已有 7 项 CI 成功,剩余 2 项 macOS 检查仍在排队;合并前请等待它们完成。

@limityan
limityan merged commit 318e059 into GCWing:main Aug 24, 2026
10 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.

2 participants