历史任务从侧边栏消失时,人很容易立刻进入“恢复数据”的思路:换一份数据库、重建索引,或者把旧目录覆盖回来。问题在于,侧边栏空白只是一种界面现象。正文文件、任务元数据、列表查询和历史分页分别位于不同层级,任何一层出现偏差,都可能制造相似的表象。

可靠的排障顺序,是先证明数据处于什么状态,再决定是否需要修复;每一次修改都应晚于相应证据

这套方法来自一次历史任务不可见故障的完整复盘。如果想先理解 rollout、SQLite、Provider 与列表之间的关系,可以阅读任务存储与生命周期模型。本文只关注遇到故障后怎样缩小范围,以及怎样避免把仍然完整的数据越修越乱。

文中的 state_5.sqlite 字段、App Server RPC 和 Provider 实验对应 0.145.0-alpha.18 的本机观察。公开环境变量以当前官方文档为准;内部表结构和客户端调用方式可能随版本变化。

先判断故障落在哪一层

“历史任务消失”可以拆成几类彼此不同的问题:

表象优先检查的层级首要证据
侧边栏没有旧任务发现层thread/list 条件、数据库任务行
归档页为空发现层archived 与 Provider 条件
项目下任务不完整项目映射或发现层项目映射、无项目集合、列表结果
已知 ID 能返回元数据定位层正常thread/read 响应、数据库行
界面或响应包含历史读取层正常includeTurns、rollout 解析
打开后只有最近几轮历史分页层turns cursor、持续向前加载
可以阅读但不能继续发送恢复与运行层Provider、认证、模型与传输
rollout 文件缺失物理数据层数据库路径与文件存在性

这张表的价值在于,它把一个模糊的“历史没了”改写成几个可以分别验证的问题。完整报错、任务 ID、文件状态和查询结果,比侧边栏截图更接近根因。

第一步永远是保护现场

在根因未知时,先停止这些操作:

  • 用旧快照覆盖当前状态目录;
  • 批量修改 SQLite;
  • 拼接或重写 rollout;
  • 同时调整 Provider、项目映射和索引;
  • 删除旧目录或迁移半成品;
  • 在 Codex 运行时替换活动数据库。

活动任务的 JSONL 可能仍在追加。一个看似“更正常”的旧快照,可能恰好缺少最近几轮对话、工具调用和最有价值的调查证据。

时间点快照至少应保存:

config.toml
state_5.sqlite 及相关 WAL 文件
sessions/
archived_sessions/
session_index.jsonl
全局状态文件
当前 Codex 版本
CODEX_HOME 与 CODEX_SQLITE_HOME
快照时间

正式快照、目录替换和激活操作应在 Codex 窗口、系统托盘和相关后台进程完全退出后进行。运行中可以做只读盘点,但它只能描述当前时刻,无法替代一致性快照。

用只读证据回答三个问题

第一轮调查只需要回答:正文有没有、元数据有没有、路径是否能把两者连起来。

rollout 是否仍在

先检查 sessions/archived_sessions/,再用任务 ID 定位文件。标题、项目名和侧边栏位置都可能变化,任务 ID 更适合作为恢复锚点。

找到候选 rollout 后,确认:

  1. 文件能够逐行解析;
  2. 首部 session_meta.payload.id 与目标任务一致;
  3. 尾部仍有预期的近期消息;
  4. 文件长度与修改时间符合任务活动情况;
  5. 多份副本之间是否存在严格前缀关系。

如果标题搜索失败,但对应 rollout 仍然存在,“物理删除”就不应继续作为首要假设。

SQLite 是否完整

对数据库副本执行只读检查:

PRAGMA integrity_check;

预期结果为:

ok

随后检查任务 ID、rollout_path、归档状态和 Provider。完整性通过只能排除部分数据库损坏,无法证明列表条件正确,也无法证明路径指向的正文仍然存在。

路径是否自洽

本机版本的 threads.rollout_path 使用绝对路径。需要逐项确认:

任务 ID 存在于 SQLite

rollout_path 指向实际文件

文件中的 session_meta.payload.id 与任务 ID 相同

数据库位于新目录、rollout 位于新目录、数据库路径指向这份 rollout,是三个需要分别成立的条件

建立假设矩阵

只读取证完成后,不要急着选择最顺眼的解释。把假设、证据和修改风险放在同一张表里:

假设支持证据排除方式风险
rollout 被删除路径缺失搜索旧根目录与快照低,只读
SQLite 损坏完整性失败PRAGMA integrity_check低,只读
项目映射损坏只在特定项目缺失无项目查询、按 ID 读取
Provider 筛选配置切换后列表变化同库、不同配置 A/B低,隔离副本
长任务被截断只显示最近几轮分页与冷启动对照
旧格式不兼容干净配置也无法读取代表性样本迁入
跨盘迁移不完整旧目录继续增长检查路径与新消息位置低,只读

每次实验都记录“支持或排除了什么”。单纯写下“失败”会丢掉最重要的信息:调查范围究竟缩小了多少。

用单变量 A/B 停止猜测

当几种假设都说得通时,最有效的实验通常很朴素:准备两个隔离环境,让它们只差一个变量。

常见组合包括:

同一数据库 + 不同 config.toml
同一 config.toml + 不同 thread/list 参数
同一任务 + 多次冷启动
同一 rollout + 有无额外历史锚点
同一迁移 stage + 不同默认 Provider

如果一次同时改动数据库、项目映射、索引、Provider 和正文,即使界面恢复,也很难知道究竟是哪一步起了作用。

Provider A/B 的实际结果

在那次故障中,三个隔离 CODEX_HOME 使用同一份数据库快照,只改变默认 Provider,列表随之切换到不同任务集合;显式查询全部 Provider 时,两组任务又同时出现。完整计数见故障复盘

回到实际配置后,再往返切换顶层 model_provider,相同任务集合随之稳定出现和隐藏。数据库与 rollout 没有变化,配置成为唯一受控变量。

隔离实验给出相关性,实际环境中的可重复往返切换补齐因果链

当前 App Server README 将未设置、null 和空数组描述为包含全部 Provider,公开仓库也有与实际行为差异相关的讨论。因此,涉及 modelProviders 的判断必须同时记录版本、请求参数和客户端入口。

项目映射与干净配置各能证明什么

任务只在某个项目中缺失时,项目映射当然值得检查。置顶、工作区提示、无项目集合和项目归属可以改变任务出现在哪一组,却未必改变列表是否返回任务。

调整项目映射后,如果任务能够换组但仍会在重启后消失,这个实验至少排除了“只修项目归属就能恢复完整历史”的假设。

干净配置则适合回答另一组问题:

  • 新版本能否创建并持久化任务;
  • 旧 rollout 能否被当前版本读取;
  • 归档、项目和父子关系能否迁入;
  • 故障是否会随旧配置重新出现。

先选择普通活动任务、归档任务、超长任务、有项目映射的任务和含工具调用的任务。代表性样本稳定后,再决定是否需要全量迁移。

别把历史分页当成正文截断

超长任务第一次打开时,客户端可能只展示最近几轮。此时先持续向前滚动,并观察更早内容是否逐步出现。

当前 App Server 还提供过实验性的分页接口:

thread/turns/list
thread/items/list

这些接口只对应特定分页模式,不能从界面现象反推出 Desktop 一定调用了它们。本机对照中,多次冷启动后继续向前加载,都能到达最早消息。原始 rollout 没有注入伪消息,也没有为了“唤醒历史”而改写正文。

页面只显示近期内容时,优先验证分页;只有分页始终无法到达、文件或解析证据也异常,才把问题升级到正文层

一棵实用的诊断树

flowchart TD
    A["历史任务不可见"] --> B["停止覆盖、清理与批量改写"]
    B --> C["确认版本、CODEX_HOME 与 SQLite 位置"]
    C --> D{"rollout 是否存在"}
    D -->|"否"| E["查找旧路径与时间点快照"]
    D -->|"是"| F{"SQLite 完整且有任务行"}
    F -->|"否"| G["在隔离副本中评估元数据重建"]
    F -->|"是"| H{"已知 ID 能否定位元数据"}
    H -->|"否"| I["检查数据库、扫描与任务身份"]
    H -->|"是"| P{"正文是否可读"}
    P -->|"否"| Q["检查 includeTurns、路径与解析错误"]
    P -->|"是"| J{"列表是否随配置变化"}
    J -->|"是"| K["单变量定位 Provider 或查询条件"]
    J -->|"否"| L["检查项目映射与客户端状态"]
    K --> M{"只显示近期历史"}
    L --> M
    M -->|"是"| N["向前分页并做冷启动对照"]
    M -->|"否"| O["检查恢复、认证、模型与传输"]

这棵树刻意把高风险修改放在后面。只要正文、元数据和路径仍然自洽,大多数前期判断都可以在副本中完成。

恢复以后怎样证明它真的好了

恢复验证至少覆盖:

检查目的
SQLite 完整性排除结构损坏
任务 ID 集合排除遗漏和意外新增
活动与归档数量检查分类
rollout 与 session_meta.payload.id验证正文身份
Provider 分布识别可见性分区
项目、置顶和父子关系检查组织结构
长任务向前分页检查历史完整性
两次冷启动排除单次缓存造成的假象
新消息写入位置验证实际运行路径

如果这次工作还包含 CODEX_HOME 迁移,最后一项尤其重要。跨盘复制、多源合并和路径协调需要独立的迁移方案,不能只依靠列表恢复结果判断完成。

参考资料