Skip to content

[Bug] Codex Usage rebuild permanently defers a valid fork when parent max timestamp predates child creation #392

Description

@Aestas16

问题描述

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。

复现步骤

  1. 确保父 rollout 和带有 forked_from_id 的子 rollout 都存在于 ~/.codex/sessions/
  2. 父 rollout 的最后事件时间早于子 rollout 的 session_meta.timestamp
  3. 在 cc-switch Usage 页面执行 Codex rebuild。
  4. 查看日志或导入结果。

实际结果

子 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 匹配判断。

建议增加以下回归测试:

  1. Parent 的最后 timestamp 比 child 创建时间早 12 秒。
  2. Child 开头完整复制 parent 的全部 token signatures。
  3. Child 随后包含新的 token events。
  4. Rebuild 应跳过复制的 parent prefix,并导入后续 live events。
  5. 再次 rebuild 不应重复计数。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions