Skip to content

fix(chat): recover lost execution workers without replay - #7970

Merged
loopx-agent merged 2 commits into
mainfrom
codex/steward-worker-exit-recovery-20261103
Nov 3, 2026
Merged

loopx-agent merged 2 commits into
mainfrom
codex/steward-worker-exit-recovery-20261103

Conversation

@loopx-agent

@loopx-agent loopx-agent commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

当执行线程因进度写入和失败收尾同时遇到存储错误而退出时,Chat 服务进程仍然可能存活。旧恢复逻辑只认进程死亡,原会话会一直保留 running,后续已保存的消息及普通升级被堵住。

本改动在既有生命周期 owner 内核验同一执行锁、锁持有记录及受管 Turn 状态,只有取得已释放的内核执行锁且记录没有更换时才收敛失联执行。原请求和部分结果保留,执行效果明确未知,不重发原输入。原队列线程在存储恢复后继续领取下一条已保存输入;普通激活可执行这段非执行性恢复,仍然对剩余排队或执行中的工作保持忙碌保护,不创建第二消费者。队列枚举和终态补写回到现有 ingress store,保持现有 API,不提高模块限制。

验证:同一最终测试夹具在真实 detached Git 基线上暴露 3 个旧故障,活跃执行锁反例通过。Ruff、队列枚举 AST 等价及标准 premerge 通过;保留未修改的 render_canonical_todo_sections 维护性告警,基线/候选的完整输出相同。五个变更文件的公开边界扫描通过;宿主汇总 check 的两个既有外部 Goal authority 错误未修饰为成功。最终 head 的激活、跨进程恢复、传输、队列、活动位、事件缓冲及等待回归共 190 项通过,另覆盖激活后新增会话、同 runtime 的 attached 会话隔离及可变摘要字段不能隐藏运行工作。

不宣称已经安装或处理完历史消息。生产采用、原 Bot 自行领取队列及原受众最终回复分别验收;效果未知的历史失败不会自动重放。当前 Goal 禁止等待 CI,未获取或轮询 CI。

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent; GPT-6 (Codex); OpenAI; self_reported

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

结论:APPROVE,精确 head f4fc03bc2fb6d97e77a57e34828b2492eca370eb 未发现阻塞问题。按已知模型家族自述;同一 Turn 的宿主身份读取不可用,这不是独立人工评审或 GitHub 正式自批准。

动机 / Motivation

同一服务 PID 仍存活,原执行线程因进度及失败收尾落盘同时失败而退出;旧版保留 running,新版核实已释放的执行锁后记录失败,原队列继续下一条已保存输入。 恢复原 FIFO 的前进条件,保留原消息、部分进度和未知执行效果;不重新发送失败输入。

Existing App and Lark Chat users with saved follow-up input and ordinary service activation. 本变更不代办业务、不重放未知效果输入、不强制重启忙碌服务、不增加 listener,也不从激活入口执行模型。 实际安装、旧服务自动接续、provider 送达和历史业务最终答复尚需独立验收;这份评审不认证生产恢复完成。

已有进程被杀恢复测试无法覆盖这个区别:进程还活着不代表其每个线程仍有执行权。文件写入失败可能同时打断进度 checkpoint 与失败处理;活动位留在磁盘上,线程的 finally 却已经释放执行锁。继续等待 PID 死亡会让后续消息永久停住,也令普通升级持续报告 busy。这里交付 justified_increment:修复原 owner 的缺失观察和队列异常存活,而不把业务补办或生产回传声称为已完成。

改动思路 / Approach

恢复只处理本机、确切执行锁身份和未更换记录下的受管 starting/running/interrupting;排队、attached、外来/未知记录和仍被持有的锁不允许推断。

没有新增 watchdog、第二消息消费者、状态文件或重试预算。恢复仍走既有 managed_turn_recovery_allowed 与 _fail_turn;物理执行锁的生命周期对应执行线程,PID 对应服务。新增规则要求本机、正整数 PID、确切 lock_id、agent/operation/policy,并在取得原非阻塞内核锁后再次确认持有记录没有更换。只有这组证据成立才能向既有 typed owner 说明执行 owner 已退出,服务 PID 本身仍可存活。失败使用现有 runtime_error,明确 execution may have occurred,不能把该失败当作可安全重发。

我比较了继续等进程退出、直接终止服务、重发输入和另建监控线程。前者保留死锁,终止服务可能中断其它原请求,重发会重复未知效果;第二消费者会产生新的同步及授权问题。当前方案保留原生命周期决定、原 FIFO 和普通激活入口。队列枚举与终态补写放回已有 ingress store,避免在大型运行/存储模块继续增加责任;这段整理没有提高模块限制。

具体改动 / Changes

依据是固定主干的 原会话/FIFO 与回执恢复契约。original-fifo-owner、unknown-effects-no-replay 与 remaining-work-guard 三项落实到代码和反例;provider-final-return 明确 out_of_scope。该文档没有赋予未知执行重放权;本次从缺失的执行所有权事实修复既有原队列前进条件。

五个既有文件共增加 374、删除 98 行,其中测试新增 236 行;生产 diff 包含两个相邻数据操作的责任归位,没有新增模块、公共命令、状态 schema、权限或执行器配置。

关键代码讲解

  1. recover_exited_turn 在原执行锁上核验持有者身份,再在锁内读回 Session、Turn 和未更换的 holder。死 PID 延续 server_restarted;同一活 PID 下已经失去物理执行权的线程报告现有 runtime_error。锁仍被持有、证据未知或被替换时,不写回原请求。
  2. _drain_session_queue 捕获执行收尾逸出的 OSError,使同一个队列线程保留接续机会。存储不可用时只重试非执行性的状态恢复;恢复可用后,原失败输入不重发,既有 FIFO 领取下一条已保存消息。该路径没有启动替代 controller 或 consumer。
  3. complete_recovered_terminalization 完成已经持久化的失败结果:分别补齐终态事件、错误消息和活动位释放,并逐项去重。runtime_error 的完整诊断 payload 匹配只用来确认该 owner 先前记录的恢复结果,不用来从自然语言推断业务状态或权限。队列枚举保留同一 store API,经 AST 对照,除局部依赖导入外与旧实现相同。
  4. managed_chat_activation_guard 在现有激活事务内执行这段数据恢复,再进行严格 nonterminal 检查。作用域 store 显式关闭与当前恢复无关的启动压缩及完成处理;默认 store 初始化仍启用二者。剩余排队或运行工作照旧阻止重启,helper 不开 adapter、不领取队列。
  5. 原队列磁盘故障回归 使用真实临时文件存储、原执行锁及原队列线程,只有模型 provider 被合成。进度与错误 flush 同时抛 ENOSPC 后,只恢复存储,不重新提交、唤醒或启动第二消费者;断言 provider 原输入仅一次,下一条原排队消息完成。

对主干的风险 / Risks

最强反例是误判仍在执行或已被替换的 worker。新逻辑不能只看 released_at、存活 PID 或活动位;实际内核锁和锁内一致性重读一起约束恢复。最终 190 项测试覆盖真实跨进程仍持锁、替换 holder、无/外来/坏证据、queued/attached 排除,以及终态事件、错误消息、claim release 分段写入失败后的幂等恢复。普通激活后新建的 managed 会话仍被鲜读覆盖,同一 runtime 内 attached 会话保持原状;移除活动指针或关闭 Session 摘要也不能隐藏仍为 running 的原 Turn,剩余工作继续阻止重启。

同一最终夹具,真实 detached Git 基线与候选 基线 候选
活 PID、退出线程,release metadata 成功/失败两类 两例仍 running,失败 两例原失败收敛,不重发
原队列进度及失败 flush 同时 ENOSPC 工作线程退出,下一条不执行 同线程在存储恢复后完成下一条
实际执行锁仍被持有 原数据不变,激活 deferred 同样不变且 deferred

基线是实际 Git worktree 中的固定来源,并复制同一最终测试文件,得到 3 个故障反例和 1 个正常反例通过;没有使用缺模块、导入失败或重新生成 golden 作为有效反例。最终 head 的 190 项回归 PASS,Ruff、队列枚举 AST 等价、标准 premerge 的 6 直接及 2 选择检查也通过,没有 blocking failure/manual hold。较早 885 项共享回归通过于整理前的检查点,未冒充此精确 head 的全量证据。

最初实现触发 runtime/store 两个模块增长失败,已通过上述责任归位修复,未提升 ceiling。保留原有 render_canonical_todo_sections 告警:真实 Git 基线与最终候选的同一命令都退出 1,完整 stdout 相同,源码不在本 diff。五个变更文件与公开 PR 草稿的内容边界扫描通过;宿主汇总 check 的两个既有外部 Goal authority 错误仍保留。未发布运行日志、私有原始材料或本机路径。

语义、默认行为与 CI

这是默认恢复修复,没有 default-off 或新增 opt-in 的声明。普通运行和启动维护保留原行为;只有已证明失联的执行、存储收尾异常和普通升级的数据恢复路径改变。没有增加用户必须确认的步骤,也没有把 readiness、安装成功或 provider 可用性当成派发许可。现有 Python runtime/storage provider 保留,typed Core 生命周期继续决定可恢复状态,没有复制一套控制面状态机。

当前 Goal 的 wait_for_ci=false,未获取、查询、轮询或等待 CI,不推断 CI 成功。实际安装、旧服务自动接续、provider 送达和历史业务最终答复尚需独立验收;这份评审不认证生产恢复完成。 持久存储持续不可用、未进入 starting/running 的请求,以及无确切执行证据的旧记录仍须保留其真实状态;本增量不承诺解决所有资源故障或批准通用重放。

我的整体评价 / Verdict

long_horizon 与 user_experience 的改进由同一个原队列实际接续得到支持,而非只由状态字段、安装或测试数量推断。修复复用执行 fence、typed lifecycle 与既有 FIFO;责任归位保持可审阅、可回滚,范围 proportionate,兼容性 retain。允许这个有界增量合入,同时保留实际服务采用和原受众最终答复验收,不能把父任务关闭。

English verdict: APPROVE at f4fc03b. Exact execution-fence recovery preserves unknown effects and original FIFO without replay or a second consumer; 190 final-head regressions and standard premerge pass. Retain the inherited advisory and unverified production adoption/final returns.

@loopx-agent
loopx-agent merged commit d1bd973 into main Nov 3, 2026
3 of 5 checks passed
@loopx-agent
loopx-agent deleted the codex/steward-worker-exit-recovery-20261103 branch November 3, 2026 06:10
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.

1 participant