问题描述
cc-switch-cli 5.9.3 的 Codex Usage importer 会在父 rollout 的最大事件时间早于子 rollout 创建时间时,将整个 fork 子会话标记为 deferred:
父 rollout ... 尚未写到 child fork 时刻
但对于正常且已经完整结束的父会话,父会话最后一条事件早于 fork 子会话创建时间是可能发生的。此时父文件不会再继续写入,因此无论执行多少次 Codex rebuild,这个子会话都会被永久暂缓,造成 Usage 少记。
环境
- cc-switch-cli: 5.9.3
- codex-cli: 0.146.0
- OS: WSL2 Linux x86_64
- Current provider metadata:
openai
新建的非 fork 官方 OpenAI 会话可以正常导入,问题集中在带有 forked_from_id 的子 rollout。
复现步骤
- 确保父 rollout 和带有
forked_from_id 的子 rollout 都存在于 ~/.codex/sessions/。
- 父 rollout 的最后事件时间早于子 rollout 的
session_meta.timestamp。
- 在 cc-switch Usage 页面执行 Codex rebuild。
- 查看日志或导入结果。
实际结果
子 rollout 被完整 deferred,并显示:
父 rollout ... 尚未写到 child fork 时刻
该子 rollout 中原本已经导入过的部分记录也可能在 rebuild 后消失。
期望结果
父 rollout 文件存在,并且子 rollout 开头复制的 token usage signature 可以完整匹配父 rollout 时,不应仅因为:
max(parent timestamp) < child creation timestamp
就判定父 rollout 不完整。
Importer 应跳过已经匹配的父历史,并导入匹配边界之后的子会话实际用量。只有父文件缺失、父子签名无法可靠匹配或内容确实不完整时才应 deferred。
本地诊断数据(已脱敏)
- Parent maximum event timestamp:
2026-07-30T10:20:29.539Z
- Child
session_meta timestamp:
2026-07-30T10:20:41.573Z
- Difference:
12.034 seconds
- Parent token usage signatures:
2,983
- Parent signatures matched in order at the beginning of child replay:
2,983 / 2,983
- Live child records after the matched prefix:
259
- Missing token usage:
- fresh input:
831,988
- cached input:
35,880,192
- output:
127,592
- total:
36,839,772
- Missing calculated cost:
approximately $25.927796
所有父 signature 都在第一个未匹配的子事件之前按顺序匹配完成,因此这 259 条是 post-fork live usage,而不是复制的父历史。
可能的原因
parent_signatures_before() 当前包含如下判断:
if max_timestamp.is_none_or(|timestamp| timestamp < cutoff) {
return Err(format!(
"父 rollout {} 尚未写到 child fork 时刻",
parent_path.display()
));
}
Relevant code:
https://github.com/SaladDay/cc-switch-cli/blob/v5.9.3/src-tauri/src/services/session_usage_codex.rs#L731-L735
这个条件把“父会话最后一次活动时间”当成了“父 rollout 完整写入到 fork 创建时间”的证明,但两者并不等价。
建议修复和回归测试
建议不要单独使用父文件最大时间戳判断完整性,而是结合父子 token signature/replay-prefix 匹配判断。
建议增加以下回归测试:
- Parent 的最后 timestamp 比 child 创建时间早 12 秒。
- Child 开头完整复制 parent 的全部 token signatures。
- Child 随后包含新的 token events。
- Rebuild 应跳过复制的 parent prefix,并导入后续 live events。
- 再次 rebuild 不应重复计数。
问题描述
cc-switch-cli 5.9.3 的 Codex Usage importer 会在父 rollout 的最大事件时间早于子 rollout 创建时间时,将整个 fork 子会话标记为 deferred:
但对于正常且已经完整结束的父会话,父会话最后一条事件早于 fork 子会话创建时间是可能发生的。此时父文件不会再继续写入,因此无论执行多少次 Codex rebuild,这个子会话都会被永久暂缓,造成 Usage 少记。
环境
openai新建的非 fork 官方 OpenAI 会话可以正常导入,问题集中在带有
forked_from_id的子 rollout。复现步骤
forked_from_id的子 rollout 都存在于~/.codex/sessions/。session_meta.timestamp。实际结果
子 rollout 被完整 deferred,并显示:
该子 rollout 中原本已经导入过的部分记录也可能在 rebuild 后消失。
期望结果
父 rollout 文件存在,并且子 rollout 开头复制的 token usage signature 可以完整匹配父 rollout 时,不应仅因为:
就判定父 rollout 不完整。
Importer 应跳过已经匹配的父历史,并导入匹配边界之后的子会话实际用量。只有父文件缺失、父子签名无法可靠匹配或内容确实不完整时才应 deferred。
本地诊断数据(已脱敏)
2026-07-30T10:20:29.539Zsession_metatimestamp:2026-07-30T10:20:41.573Z12.034 seconds2,9832,983 / 2,983259831,98835,880,192127,59236,839,772approximately
$25.927796所有父 signature 都在第一个未匹配的子事件之前按顺序匹配完成,因此这 259 条是 post-fork live usage,而不是复制的父历史。
可能的原因
parent_signatures_before()当前包含如下判断:Relevant code:
https://github.com/SaladDay/cc-switch-cli/blob/v5.9.3/src-tauri/src/services/session_usage_codex.rs#L731-L735
这个条件把“父会话最后一次活动时间”当成了“父 rollout 完整写入到 fork 创建时间”的证明,但两者并不等价。
建议修复和回归测试
建议不要单独使用父文件最大时间戳判断完整性,而是结合父子 token signature/replay-prefix 匹配判断。
建议增加以下回归测试: