Skip to content

fix(sessions): WSL 模式下删除会话假成功,rollout 文件未删除 (#162) - #2361

Closed
dongyu23 wants to merge 1 commit into
BigPizzaV3:mainfrom
dongyu23:codex/fix-wsl-rollout-delete
Closed

dongyu23 wants to merge 1 commit into
BigPizzaV3:mainfrom
dongyu23:codex/fix-wsl-rollout-delete

Conversation

@dongyu23

Copy link
Copy Markdown
Contributor

Fixes #162

问题现象

WSL 模式下在 Codex++ 里删除会话,界面提示删除成功(local_deleted),但重启 Codex App 后会话重新出现在侧边栏。备份 json 里有 tables.threads 记录,却没有 tables.__files 段。

根因

WSL 模式中 codex 写入 state_5.sqlite 的 threads.rollout_path 是 WSL 视角路径(如 /mnt/c/Users/.../rollout-xxx.jsonl),而 Codex++ 的 helper 是 Windows 侧进程,按该路径找不到文件。断链发生在 crates/codex-plus-data/src/storage.rs 的 rollout_file_backups:

let bytes = fs::read(path).ok()?;   // 读不到 → 静默返回 None,文件被从备份列表剔除

连锁反应:__files 为空 → 文件删除循环遍历空列表(不删、零报错)→ 返回 local_deleted 假成功 → 数据库行已删、rollout 文件残留 → Codex 重启时从残留文件重建会话。

我在 Windows 真机上用 A/B 对照实验复现过:同一个文件、同一张表,唯一变量是 rollout_path 写 Windows 视角还是 WSL 视角——前者真删除,后者假成功、文件原样保留。

修复方案

改动集中在 crates/codex-plus-data/src/storage.rs:

  1. 新增 wsl_path_alternative:/mnt/<盘符>/... 与 <盘符>:/... 两种路径视角互转(纯字符串转换,双向可逆)。
  2. rollout_file_backups 先按原路径读取,失败再尝试互转视角;备份条目的 path 使用可读写视角(供删除与撤销直接使用),两视角不同时附带 source_path 保留 DB 原始写法。
  3. 文件存在但两种视角均读取失败时并入 file_errors:数据库删除后返回 Failed 并附 undo token,不再静默报成功——这是 WSL 模式下删除会话提示成功,但重启后会恢复 #162 明确要求的行为(「如果 rollout 文件读不到或删不掉,也不要返回删除成功」)。
  4. undo 白名单(allowed_backup_file_paths)同时放行两种视角:保证 WSL 会话删除后可以完整撤销(数据库行 + rollout 文件都能还原)。放行集仍要求目标不存在、仍只能来自 DB 里真实存在的 rollout_path,不扩大可写位置。

兼容性:两种视角下文件均不存在时(如已被手动清理)维持旧行为——跳过文件、删除成功;Windows 视角路径的删除行为完全不变(有对照回归测试)。

测试

  • wsl_path_alternative 单测 4 个:双向转换、往返稳定、非法形式拒绝。
  • 回归测试 3 个(tests/wsl_rollout_path.rs):
    • WSL 视角路径 → 真删除 + 备份含 __files/source_path + 撤销完整还原(#[cfg(windows)]);
    • Windows 路径对照组行为不变;
    • 文件缺失兼容用例。
  • 既有 27 个 storage 测试零退化;cargo test -p codex-plus-manager 中 delete_local_session 相关测试通过;clippy 无新增警告。
  • 另做了真机端到端验证:构建修复后的 manager 二进制,沙箱环境伪造 WSL 场景,通过 WebView2 CDP 调用真实 delete_local_session 命令(与 GUI 删除按钮同一入口)——返回 local_deleted、rollout 文件确实从磁盘消失、备份含 __files、二次删除返回 Thread not found in local storage。

这个 bug 为什么被埋没了四个月

复盘下来不是没人看见,而是几层因素叠加,写出来供后续测试建设参考:

  1. 复现门槛高:需要「Windows + Codex 开 WSL 模式」这个组合环境,维护者和多数贡献者不在其中。issue 下长期只有一条同类反馈,信号弱。
  2. 静默失败不产生任何错误信号:.ok()? 把寻址失败吞掉,日志里只有一路成功(local_deleted)。没有报错就没有排查入口,用户侧只能观察到「重启后复活」这个间接症状。
  3. 测试网格的系统性盲区:现有删除测试全部是「把本地视角路径直接写进 sqlite」的夹具——DB 里的路径和进程视角永远一致,「数据库记录的路径与进程视角不一致」这类场景在测试里不可见。修完照样全绿,没人知道这个坑还在。
  4. 修复潮反复路过它:6~9 月删除功能其实经历了多轮修复(session_index.jsonl 同步、侧边栏索引、撤销刷新等),但每轮修的都是贡献者自己环境里能复现的症状;storage.rs 最近一次改动(9 月初)就在这条流程上,只是 WSL 变体在他们的环境里不存在。

其中第 3 点或许值得后续补一类「跨视角路径」的测试夹具;本 PR 的 wsl_rollout_path.rs 已是这类夹具的第一个样例。

贡献确认

  • 按 CONTRIBUTING.md:新增测试齐全、cargo fmt(改动区域)、clippy 无新增警告、既有测试全绿。
  • 提交内容最小化:仅 storage.rs 语义改动 + 新测试文件,不含无关格式化噪音。
  • 许可:AGPL-3.0-only。

WSL 模式中 codex 写入 threads.rollout_path 的是 WSL 视角路径
(/mnt/<盘符>/...),Windows 侧 helper 按原路径读不到文件:
rollout_file_backups 里 fs::read(path).ok()? 静默跳过,导致备份缺
__files、文件删除循环空转,最终返回「已从本地存储删除」的假成功,
Codex 重启后从残留的 rollout 文件重建会话(删除后复活)。

- 新增 wsl_path_alternative:/mnt/<盘符>/... 与 <盘符>:/... 视角互转
- rollout_file_backups 先按原路径读取,失败再尝试互转视角;__files
  条目的 path 用可读写视角并保留 source_path 原始写法
- 文件存在但两种视角均读取失败时并入 file_errors,不再静默成功
- undo 白名单同时放行两种视角,WSL 会话可完整撤销
- 兼容:两种视角下文件均不存在时维持旧行为(跳过并删除成功)

测试:wsl_path_alternative 单测 4 个;回归测试 3 个(真删除 + 备份/
撤销完整、Windows 路径对照、文件缺失兼容);既有 27 个 storage
测试无退化;另在 Windows 真机通过 manager 二进制 + CDP 驱动真实
delete_local_session 命令做了端到端验证。
@BigPizzaV3

Copy link
Copy Markdown
Owner

改动已合入 main,关闭本 PR。

说明:合入时对分支做了 rebase,SHA 与 PR 原始提交不同,GitHub 无法自动识别为已合并。

这个修复很扎实 —— 根因链(fs::read(path).ok()? 静默吞掉失败 → __files 为空 → 删除循环遍历空列表 → 返回 local_deleted 假成功)定位准确,A/B 对照实验把变量控制在了「路径视角」一个因素上,方案也符合 #162 明确要求的「读不到就不要报成功」。

一处想请你确认:wsl_rollout_path.rs 里最关键的回归测试 wsl_rollout_path_delete_removes_file_and_undo_restores 带 #[cfg(windows)],因此在 macOS/Linux 上不会执行。我这边(macOS)验证到的是:

  • wsl_path_alternative 的 4 个跨平台单元测试通过(正反向转换、往返稳定、拒绝两侧之外的路径)
  • 另两个不带 cfg 的用例通过(Windows 视角对照组、文件缺失仍成功)
  • codex-plus-data 全量 193 passed / 0 failed

也就是说核心的路径转换逻辑已被覆盖,但「WSL 视角下真的删掉文件并撤销还原」这条端到端行为只能在 Windows 上跑到。如果这条路径对 Windows CI 有依赖,请确认 CI 上是绿的。

@BigPizzaV3 BigPizzaV3 closed this Sep 30, 2026
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.

WSL 模式下删除会话提示成功,但重启后会恢复

2 participants