Skip to content

fix: stream historical session repair - #2084

Open
liulinboyi wants to merge 5 commits into
BigPizzaV3:mainfrom
liulinboyi:fix/history-chat
Open

fix: stream historical session repair#2084
liulinboyi wants to merge 5 commits into
BigPizzaV3:mainfrom
liulinboyi:fix/history-chat

Conversation

@liulinboyi

@liulinboyi liulinboyi commented Sep 2, 2026

Copy link
Copy Markdown

问题

“修复历史会话”的批量 provider 同步在 Codex 历史会话较多时,会将多个 rollout JSONL 完整读入内存,同时保存原文、改写文本和回滚副本,内存占用会随全部历史会话正文规模增长。

同一流程后续的 session_index.jsonl 清理也会同时保留完整字节、完整文本并构造完整的新文本,可能产生第二次内存峰值。另外,管理器此前使用定时器模拟进度,展示进度与后端实际处理情况不一致。

改动

  • 新增 atomic_write_with 流式原子写入 helper,支持分块写入临时文件、原子替换、保留原文件权限,并兼容现有 atomic_write
  • 批量 provider sync 改为“两阶段”:
    • 逐行扫描 rollout JSONL,只保留轻量 rewrite plan、thread 元数据、mtime 和 SHA-256。
    • 仅对确实需要修改的文件进行逐文件、逐行流式改写,不再缓存全部 rollout 原文。
  • 只修改有效 session_meta 中的 model_provider,非 metadata 行、未知 JSON、无效 JSON、行结束符和其他正文内容原样保留。
  • 写入前重新校验源文件 SHA-256;文件被外部修改时跳过,不覆盖新内容。
  • 区分锁定/共享冲突与普通 IO 错误;锁定文件记录到 skipped_locked_rollout_files,其他错误会触发已应用文件的回滚。
  • 回滚改为基于原始 session_meta 行、数量/顺序校验和 hash 校验,不再保存整份原始 rollout;同时恢复原始 mtime 和权限。
  • session-meta-backup.json 改为增量写入,保持原有 JSON schema 和兼容格式。
  • session_index.jsonl 的 preview/apply 清理改为流式读取和写入,保留未知记录、候选确认和快照变化保护语义。
  • 新增真实 provider sync progress API,支持 scanning、planning、backing_up、rewriting、updating_indexes、rolling_back、complete 阶段。
  • Tauri 通过定向事件向当前管理器窗口发送进度;前端移除模拟 setInterval,改为监听后端真实计数并映射进度。
  • 保留 run_provider_syncrun_provider_sync_with_targetProviderSyncResult 及 Remote Control 单会话恢复路径的兼容行为。
  • 将环境变量扫描改为 vars_os() 容错处理,避免 macOS 上存在非 UTF-8 环境变量时导致 manager 崩溃。

测试

  • cargo test --workspace:通过
  • cargo test -p codex-plus-data --test provider_sync:49 passed
  • cargo test -p codex-plus-core settings::tests:46 passed
  • cargo test -p codex-plus-core env_conflicts::tests:3 passed
  • npm test:122 passed
  • npm run check:通过
  • npm run vite:build:通过
  • cargo check -p codex-plus-manager:通过
  • cargo build --release:通过
  • git diff --check:通过
  • 本地 Apple Silicon DMG 已生成,并通过 hdiutil verify 与 App 深度签名验证。
  • 全量 cargo fmt --all -- --check 仍会报告仓库现有格式差异,本次未做无关的格式化重排。
  • 自己手动测试了下,可以正常使用

涉及文件

  • crates/codex-plus-core/src/settings.rs
  • crates/codex-plus-core/src/env_conflicts.rs
  • crates/codex-plus-core/src/model_catalog.rs
  • crates/codex-plus-core/src/relay_environment.rs
  • crates/codex-plus-data/src/provider_sync.rs
  • crates/codex-plus-data/src/lib.rs
  • crates/codex-plus-data/tests/provider_sync.rs
  • apps/codex-plus-manager/src-tauri/src/commands.rs
  • apps/codex-plus-manager/src/App.tsx
  • apps/codex-plus-manager/src/provider-sync-flow.ts
  • apps/codex-plus-manager/src/provider-sync-flow.test.ts
  • docs/plans/2026-08-27-历史会话流式修复实施计划.md

Problem

The batch provider synchronization used by “Repair Historical Sessions” loaded multiple rollout JSONL files fully into memory and retained original text, rewritten text, and rollback copies. When a Codex installation contained many historical sessions, memory usage grew with the total size of all stored session content.

The subsequent session_index.jsonl cleanup also retained the full file and constructed a complete rewritten copy, creating a second memory peak. In addition, the manager previously displayed simulated timer-based progress instead of progress based on actual backend work.

Changes

  • Added atomic_write_with for streamed atomic file writes with chunked output, atomic replacement, and preservation of existing file permissions.
  • Reworked batch provider synchronization into two phases:
    • Scan rollout JSONL files line by line and retain only lightweight rewrite plans, metadata, mtimes, and SHA-256 hashes.
    • Rewrite only files that actually require changes, one file and one line at a time.
  • Only update model_provider in valid session_meta records. Preserve all non-metadata lines, unknown or invalid JSON, line endings, and other rollout content byte-for-byte.
  • Recheck the source file SHA-256 before writing. Skip files changed by another process instead of overwriting newer content.
  • Distinguish locked/shared-conflict files from other IO failures. Locked files are reported through skipped_locked_rollout_files; other failures trigger rollback of files already applied in the current operation.
  • Implement rollback using the original session_meta lines, count/order validation, and hashes instead of retaining complete rollout files. Restore original mtimes and permissions as well.
  • Write session-meta-backup.json incrementally while preserving the existing JSON schema and compatibility.
  • Convert session_index.jsonl preview/apply cleanup to streamed reads and writes while preserving unknown records, candidate confirmation, and snapshot-change protection.
  • Added real provider-sync progress reporting with scanning, planning, backing_up, rewriting, updating_indexes, rolling_back, and complete phases.
  • Send progress through a Tauri event targeted at the initiating manager window. The frontend now listens to real backend counts and no longer uses simulated setInterval progress.
  • Preserve the existing run_provider_sync, run_provider_sync_with_target, ProviderSyncResult, and Remote Control single-session recovery behavior.
  • Switched environment scanning to vars_os() so non-UTF-8 macOS environment variables do not crash the manager.

Tests

  • cargo test --workspace: passed
  • cargo test -p codex-plus-data --test provider_sync: 49 passed
  • cargo test -p codex-plus-core settings::tests: 46 passed
  • cargo test -p codex-plus-core env_conflicts::tests: 3 passed
  • npm test: 122 passed
  • npm run check: passed
  • npm run vite:build: passed
  • cargo check -p codex-plus-manager: passed
  • cargo build --release: passed
  • git diff --check: passed
  • Built a local Apple Silicon DMG and verified it with hdiutil verify and deep App signature checks.
  • Full cargo fmt --all -- --check still reports existing repository formatting differences; no unrelated formatting-only changes were introduced.
  • I tested it manually and it works normally.

Files

  • crates/codex-plus-core/src/settings.rs
  • crates/codex-plus-core/src/env_conflicts.rs
  • crates/codex-plus-core/src/model_catalog.rs
  • crates/codex-plus-core/src/relay_environment.rs
  • crates/codex-plus-data/src/provider_sync.rs
  • crates/codex-plus-data/src/lib.rs
  • crates/codex-plus-data/tests/provider_sync.rs
  • apps/codex-plus-manager/src-tauri/src/commands.rs
  • apps/codex-plus-manager/src/App.tsx
  • apps/codex-plus-manager/src/provider-sync-flow.ts
  • apps/codex-plus-manager/src/provider-sync-flow.test.ts
  • docs/plans/2026-08-27-历史会话流式修复实施计划.md

@BigPizzaV3

BigPizzaV3 commented Sep 2, 2026

Copy link
Copy Markdown
Owner

这次改动涉及 Provider Sync 流式重写、原子写入、回滚和真实进度,范围较大。当前 PR 没有 GitHub CI 结果,请先触发并跑完 Windows、macOS x64、macOS arm64 构建及相关 workspace 测试,重点确认锁冲突、外部修改保护、部分失败回滚和 session_index 流式清理语义。CI 全绿后再复审。

@liulinboyi

Copy link
Copy Markdown
Author

这次改动涉及 Provider Sync 流式重写、原子写入、回滚和真实进度,范围较大。当前 PR 没有 GitHub CI 结果,请先触发并跑完 Windows、macOS x64、macOS arm64 构建及相关 workspace 测试,重点确认锁冲突、外部修改保护、部分失败回滚和 session_index 流式清理语义。CI 全绿后再复审。

@BigPizzaV3 现在无法进行测试,此工作流程需要维护者批准

@BigPizzaV3 BigPizzaV3 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

当前分支已与 main 冲突,且改动范围较大(provider sync、SQLite、manager 进度和回滚)。请先 rebase 到最新 main,保留未提交用户数据兼容性,补跑三平台 CI,并重点说明删除/undo 索引修复(#2092)与历史 provider 同步的交互;通过后再复审。

@liulinboyi

Copy link
Copy Markdown
Author

当前分支已与 main 冲突,且改动范围较大(provider sync、SQLite、manager 进度和回滚)。请先 rebase 到最新 main,保留未提交用户数据兼容性,补跑三平台 CI,并重点说明删除/undo 索引修复(#2092)与历史 provider 同步的交互;通过后再复审。

@BigPizzaV3 已按意见处理:

  1. 已将 fix/history-chat rebase 到最新 main48d4315),并使用 --force-with-lease 更新分支。当前分支以该 main 为祖先,PR 合并冲突已消除。

  2. 已重新触发三平台构建,最终提交为 027ca05
    https://github.com/liulinboyi/CodexPlusPlus/actions/runs/33960817266

    当前 CI 正在运行 Windows、macOS x64、macOS arm64。

  3. 本地回归已通过:

    • cargo test --workspace -- --quiet --format=terse
    • cargo test -p codex-plus-data --test provider_sync:51 passed
    • npm test:163 passed
    • npm run check
    • npm run vite:build
    • git diff --check
  4. 关于 fix: 删除会话时同步清理侧边栏索引 #2092 的删除/undo 索引修复:

    • 本次 rebase 所基于的 main 尚未包含 fix: 删除会话时同步清理侧边栏索引 #2092,因此 Provider Sync 不会调用删除或 undo 路径,也不会主动恢复已删除会话。
    • fix: 删除会话时同步清理侧边栏索引 #2092 按 thread id 删除/恢复 session_index.jsonl 与侧边栏引用;Provider Sync 仅在同步成功后,对用户明确确认的幽灵索引候选执行清理。
    • 两者共享 session_index.jsonl 时均会在写入前校验快照,并采用原子写入;文件已被另一操作修改时会中止/跳过,不覆盖新内容。
    • 我已补强 .codex-global-state.json 的兼容性:Provider Sync 保留 fix: 删除会话时同步清理侧边栏索引 #2092 使用的线程侧边栏字段;若删除/undo 在 Provider Sync 写入前更新该文件,Provider Sync 会检测到快照变化并拒绝覆盖,而不是用旧状态回写。
    • 新增测试覆盖“保留侧边栏字段”和“拒绝覆盖更新后的侧边栏状态”。
  5. fix: 删除会话时同步清理侧边栏索引 #2092 分支做了三方合并预检:provider_sync.rs 没有逻辑内容冲突;若 fix: 删除会话时同步清理侧边栏索引 #2092 先合入,剩余的是 lib.rs 的导出集合合并,会保留双方导出后再 rebase 验证。

@BigPizzaV3 BigPizzaV3 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

复审通过。已核实:本地 provider_sync 51/51 通过(与你声称一致);分支已 rebase 到 48d4315,与最新 main(已含 #2092 侧边栏索引清理的 codex-plus-data 改动)三-way merge 无冲突;流式重写、原子写入、回滚、session_index 清理语义及与 #2092 的交互均已按意见处理。虽 fork 分支本地无显示 GitHub checks,但本地验证充分。予以合并。建议后续让 fork 分支触发一次三平台 CI 以留档。

@BigPizzaV3

BigPizzaV3 commented Sep 9, 2026

Copy link
Copy Markdown
Owner

复审结论:本地 provider_sync 51/51 通过、改动本身没问题,但你 rebase 到的 base(48d4315)已过时。当前 main(48ac5f76)在你 rebase 后已合入 #2092(codex-plus-data 侧边栏索引清理)、#2093#2098(launcher 平台重试预算)等。我实测把当前分支并入当前 main:tests/launcher.rs 与 crates/codex-plus-data/src/lib.rs 出现真冲突(launcher.rs 因 #2098 改了同名测试;lib.rs 因 #2092 改了 storage/sidebar)。请基于最新 main rebase,处理好这两处与 #2092/#2098 的交互后重跑三平台 CI;冲突解决后我立即复审通过。解决冲突即可,勿动其它逻辑。

@BigPizzaV3

Copy link
Copy Markdown
Owner

提醒:之前已 approve,但当前 head 与最新 main 仍冲突(4 个文件未解决),state=DIRTY,无法合并。刚实测合并冲突在:\n\n- crates/codex-plus-data/src/provider_sync.rs(含已合入的 #2110 provider-sync 改动)\n- crates/codex-plus-data/src/lib.rs(含 #2092)\n- crates/codex-plus-core/tests/launcher.rs(含 #2098)\n- apps/codex-plus-manager/src/App.tsx\n\n请基于最新 main(含 #2097/#1712 之后)rebase 并解决这 4 处,push 后我重跑三平台 CI 即合并。

@BigPizzaV3

Copy link
Copy Markdown
Owner

@liulinboyi 催一下:当前 head 与 main 仍冲突(provider_sync.rs / lib.rs / launcher.rs / App.tsx 四文件)。请基于含 #2097/#1712 的最新 main rebase 解决后 push。

@BigPizzaV3

Copy link
Copy Markdown
Owner

我用当前 main(含新合并的 #2110/#2092/#2098)做了权威 merge 测试,4 处冲突仍在。其中 provider_sync.rs 那处是架构性分歧,不是普通 merge 能解的,需要重建。逐项说明:

1. crates/codex-plus-data/src/provider_sync.rs —— 核心(最难)

你的分支基于较老的 provider_sync.rs,引入了 bulk 批量架构(create_bulk_backup + apply_bulk_session_rewrite_plans + create_backup_with_session_meta_lines)。但 main 之后删掉了这套 bulk 函数,重构为每会话逐个处理(create_backup + apply_session_changes,在 run_provider_sync_with_target_in_home 里循环)。这两个架构互斥,merge 时整个 run_provider_sync_with_target_in_home 函数体都是冲突。

修法:把你 fork 里那些 bulk 函数(create_bulk_backupapply_bulk_session_rewrite_planscreate_backup_with_session_meta_lines)的意图移植到 main 的每会话循环里。核心意图是:

  • 你给历史会话修复加了进度上报(BackingUp/UpdatingIndexes 阶段)
  • 你处理了"扫描时跳过锁定文件"(scanned_skipped_rollout_files
  • 以及 side-effect 里的"保护 sidebar 状态"

这些在 main 的 run_provider_sync_with_target_in_home 里目前没有对应物,需要在它的循环里补上你原有的逻辑,而不是把两条函数体合起来。

2. crates/codex-plus-data/src/lib.rs —— 相对小

冲突在 re-export 列表。你导入了 ProviderSyncProgressProviderSyncProgressPhase——这两个类型 main 的 provider_sync.rs仍然存在(是 pub),只是 main 没从 lib.rs 重新导出。你把这两个加到 import 即可,同时保留 main 新增的 remove_thread_sidebar_references/ThreadSidebarCleanupResult#2098 加的,别丢掉)。

3. apps/codex-plus-manager/src/App.tsx —— 小

导入列表冲突。你新增了 resolveProviderNameproviderSyncStreamPercentProviderSyncStreamProgress 等,main 那边新增了 isProviderSyncTargetSelectable/preferredProviderSyncTarget。这两个都是新功能不互斥——合成一份 import 把两边都要。但注意:你要确认 providerSyncStreamPercent 在 main 的 provider-sync-flow.ts 里还存不存在,main 改过这个文件,函数名可能变了。

4. crates/codex-plus-core/tests/launcher.rs —— 语义重构

冲突是一个测试:你命名为 a_busy_floating_helper_port_uses_platform_retry_policy,main 改为 a_busy_floating_helper_port_respects_the_platform_retry_budget,且断言逻辑不同(main 用 macOS 31 次重试计数,你改用 busy_attempts > 1 宽松断言)。这两版本语义不一致(main 是后来合并的 #2079/#2092 相关改动),采用 main 的版本(你已有的改动如果只是重命名,就丢弃,因为 main 已重写)。

重基建议git rebase origin/main 而不是 git merge。你的 branch 落后 main 37 个 commit,rebase 后能看清每个冲突,避免把 main 的改动漏掉。

fork 那个 static 或进度百分比监听(provider-sync-progress Tauri event)是否还兼容 main 的每会话架构,需要你确认——main 有没有把进度一路传到前端 provider-sync-flow.ts,别让这处监听断开。

要不要我基于当前 main 帮你重建 provider_sync.rs 那些逻辑(把 bulk 意图移植进每会话循环),出一版补丁给你 review?你给我 maintainerCanModify 或 push 权限即可。

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