Codex 后台任务已结束,为何仍不能立即继续?

作者:袖梨 2026-09-20

把调查交给 Codex 后继续修改仓库,等后台任务结束再取回结果,看起来是一条顺畅的工作流。但任务已结束、结果可读取和输出仍适用于当前代码,其实是三件不同的事。要可靠地接着工作,需要先确认任务身份与状态,再检查工作区变化,并分清结果读取、任务恢复和会话迁移的边界。

Codex 后台任务结束了,为什么还不能直接接着用?

假设你在 Claude Code 里把一个调查交给 Codex,随后继续修改同一仓库。过了一会儿,你输入 /codex:result,拿到一份输出,准备按里面的建议接着改。

此时最容易漏掉的问题是:拿到的究竟是哪次任务?它正常完成了吗?它面对的代码还是现在这一版吗?

在本文核对的 openai/codex-plugin-cc 实现里,能取回结果,只说明找到了符合条件的结束记录;接着使用它之前,还要核对任务身份、结束状态和当前工作区。 如果打算换到 Codex 继续,会话导入又是另一项动作。

下面先看一个离线实验。它直接运行官方任务选择函数,输入是人工构造的本地记录,没有启动模型、后台 worker 或真实会话迁移。源码固定为 db52e28f4d9ded852ab3942cea316258ae4ef346,2026-09-20 重新查询官方 main 时仍为该提交;本文不把它泛化成所有 Codex 产品的行为。官方源码(本地离线实验,5项断言通过;人工记录与官方函数,非真实任务)

result 选中的,可能是最近失败的任务

有结果,不代表成功

实验给同一会话放入两条已结束记录:较早的一条是 completed,较新的一条是 failed。不提供 job ID 调用官方 resolveResultJob,得到的是较新的失败记录。

人工输入官方函数的实际返回
同一会话较早 job-a-old 已完成,较新 job-a-new 已失败默认 result 选择 job-a-new,状态为 failed
单条 job-cancelled,状态为 cancelled指定 ID 的 result 仍能选中该记录

这不是模型给错了答案。job-control.mjs 的筛选条件本来就接受 completedfailedcancelled;无 ID 时,先按当前会话筛选,再按 updatedAt 从新到旧找已结束任务。官方源码

因此,失败记录有输出、取消记录能被读取,都不应被理解为成功。result 的后续处理会读取存储文件,展示结果或错误;本实验只验证“选中哪条记录”,没有伪造一份成功的模型回答。官方源码(本地离线实验,5项断言通过;人工记录与官方函数,非真实任务)

实际操作时,把启动时返回的 job ID 留下来,后续围绕同一个 ID 查询:

/codex:status <job-id>
/codex:result <job-id>

这里的 <job-id> 需要替换成真实 ID。先看状态,再读输出与错误,最后判断它能否支持下一步动作。源码中的成功状态叫 completed,不要把自己画状态图时习惯使用的 succeeded 当成插件原始字段。

同一仓库的两个窗口,不一定看到同一张任务表

离线实验实际返回了什么

继续给实验加入两个会话:A 有两条活跃任务,B 有一条活跃任务。环境里的当前会话 ID 设置为 A。

调用 buildStatusSnapshot(..., {all: true}) 后,活跃任务列表只有 A 的两条;B 的任务没有出现。再用 B 的完整 job ID 调用 buildSingleJobSnapshot,则能读到 B 的那条记录。(本地离线实验,5项断言通过;人工记录与官方函数,非真实任务)

区别来自两条读取路径:不传 ID 的列表先用当前会话 ID 筛选;传 ID 的单条查询从该工作区的任务列表匹配。--all 改变的是列表数量限制,不能据此推断它会取消会话筛选。当没有当前会话环境变量时,默认列表才不会做这一步会话过滤。官方源码

这解释了一类容易误判的现象:换个 Claude 窗口后,列表里没看到任务,不足以说明任务丢了。先确认工作区,再尝试完整 ID。显式 ID 可以跨当前会话筛选,也说明这里的筛选更像显示范围,不能把它当成账户间的权限隔离。

取消任务时,这个区别同样有用。实验中 A 同时有两条活跃任务,不传 ID 调用官方取消目标选择函数,返回:

Multiple Codex jobs are active. Pass a job id to /codex:cancel.

这是本次离线运行保留下来的原始错误,尚未执行任何真正的取消。它要求你选定任务,不会替你猜测“最近看起来像要停的那一条”。(本地离线实验,5项断言通过;人工记录与官方函数,非真实任务)

cancelled 记录不能代替文件检查

取消之后,文件还要检查

选对任务以后,还要区分“停止执行”和“撤销已发生的工作”。

在固定版本的 handleCancel 中,处理顺序是读取 thread/turn 标识、尝试请求中断、终止记录的进程树,然后把本地 job 更新为 cancelled。返回数据还分别包含 turnInterruptAttemptedturnInterrupted。官方源码

这段代码没有回滚 Git 工作区的步骤。即使中断尝试没有成功,本地取消状态的写入也不以 turnInterrupted 为真作为前置条件。因此,仅凭 cancelled 一词,不能证明服务端 turn 已确认停止,更不能证明此前写过的文件恢复了原样。

这条结论来自源码静态核对;本次没有运行真实取消、进程终止或远端中断实验。

回到开头的教学场景:如果调查过程中曾明确允许修复,取消之后应先检查当前文件,再决定保留或返工。可以从这些只读命令开始:

git status --short
git diff --stat
git diff --cached --stat

两份 diff 统计分别覆盖未暂存和已暂存的跟踪文件改动,status 还能提示未跟踪文件;随后再展开相关文件的 diff。它们帮助确认仓库现状,不能追溯所有外部副作用。不要因为任务显示取消,就直接运行会覆盖当前修改的清理命令。

反过来,completed 也只描述执行的结束状态。如果你在调查期间改了目标文件,旧输出仍可能有参考价值,但里面的行号、失败条件和修复建议要对照新代码。最省事的做法是把那条具体发现重新核实;如果变更范围已经明显扩大,再重跑对应审查。无需每次都重建整套后台任务系统。

resume 继续已有任务,transfer 导入另一段会话

继续调查,还是移交会话

确认当前文件之后,才轮到“下一步在哪继续”。这里要分清两种上下文。

已有 Codex rescue 调查,需要沿着原问题追问时,可以使用 /codex:rescue --resume。固定版本的命令说明要求检查当前 Claude 会话中可继续的 rescue 记录;没有明确指定 --resume--fresh 时,发现候选才让用户选择继续或新建。--background--wait 则控制 Claude Code subagent 的执行方式,命令说明要求不要把它们转交成底层 task 的执行参数。官方源码

如果要把 Claude Code 里的对话移入 Codex,则是 /codex:transfer。它解析 Claude 的 JSONL 会话文件,调用导入逻辑,返回 thread ID 以及 codex resume <session-id>。导入函数还会检查是否记录了可定位的导入线程;无法找到时会报错。官方源码官方源码

当前要保留的东西对应动作仍需自行核对
已有 rescue 的 Codex 调查上下文resume 既有任务是否选对调查,以及代码是否变化
Claude Code 的对话历史transfer 后在 Codex resume导入线程、当前目录与文件现状
已结束任务的输出或错误result 指定 job ID结束状态、证据与适用版本

从这条 transfer 调用路径能确认的是“导入会话并返回可继续的线程”。其中没有搬运 Git 工作区、复制后台进程或回滚文件的动作。因此不能把“拿到 resume 命令”理解成文件、进程和执行权限已经整体搬过去。

源码还限制源会话文件必须位于解析后的 Claude projects 目录内,并使用 .jsonl 后缀;仅给它一份任意位置的同名文件并不满足这项检查。Codex runtime 是否支持导入也会影响实际运行。官方源码 本文没有读取个人会话文件,没有执行真实导入,因此不报告会话迁移成功。

交接时留下一份能核对的记录

交接时留下可核对的输入

对大多数一次性调查,不必引入队列服务。保留下面这份简短记录,足以把“拿到了输出”推进到“知道怎么继续”。这是作者建议的人工交接格式,不是插件保证自动生成的字段。

目标:这次调查要回答哪个问题
任务:job ID;结束状态;关联 Codex thread ID(如有)
输入:工作目录;开始时的提交;当时未提交改动的留存位置
当前:相关文件是否变化;已有输出哪些仍成立、哪些要重验
下一步:由谁继续;能否改文件;先核实哪一条发现

开始时的提交可以用 git rev-parse HEAD 记录,但 HEAD 无法代表未提交修改。已有暂存和未暂存 diff 要分别留存;新增文件还需另行记录。只给接手者一个提交号,却让他继续分析此前存在的未提交代码,会留下输入缺口。

如果任务仅为只读调查,结果对应的输入未变,通常核对发现后就能继续。如果执行期间允许修改、取消结果不明确,或者已换了工作区,先查文件与执行状态,再恢复会话。

下一次输入 /codex:result 时,先拿启动时的 job ID 对上记录。能对上任务,才能继续判断那份输出是否还适合手里的代码。

实验复现:从上述固定提交解压官方仓库,运行本文配套的 probe.mjs。实验输出和脚本随本地交付保留;不调用模型、worker或个人会话。

相关文章

精彩推荐