Repository navigation
fix(chat): preserve completed delivery and stop ownership - #7977
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; GPT-6 (Codex); OpenAI; self_reported
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
结论:APPROVE,精确 head 480031a087894e945a1ed9d4ab6270199bcbc3d0 未发现阻塞问题。模型身份按已知家族自述;宿主身份读取 unavailable/session_not_found,本记录不是独立人工评审或 GitHub 正式自批准。
动机 / Motivation
原 Core 已提交完整答复,但外部恢复先准备执行上下文或队列;另一路正常停止已释放执行锁,却被失联恢复误报为失败。 已完成答复直接进入原投递流程,发送前仍核验来源;正常停止由原停止调用提交结果,不被失联恢复抢占。
既有外部 Chat 用户及其原受众,以及正常停止受管任务的用户。 不代办业务、不重放未知效果输入、不新增消费者、不扩大权限、不强制重启忙碌服务。 实际服务采用、原受众完整读回和历史业务结果仍须独立验收;测试与合入不等于积压已清空。
这是两个有实证的 Chat 责任边界缺陷。外部 recover 对普通 completed 输入仍先解析执行绑定、准备工作目录并恢复执行队列,已提交的答复会被另一次执行准备拖住。先前的失联执行恢复还把正常 Stop 的短暂收尾窗口当作执行线程异常退出:worker 先释放执行锁,调用者随后才持久化 interrupted,恢复循环可能抢先写 failed。
改动思路 / Approach
原 Core 生命周期与原传输分工不变:完成答复交投递,排队输入交原队列,活服务的停止收尾保留给停止调用。
复用 completed-command 的投递责任边界、现有来源核验、执行锁与进程存活检查,不增加并行状态机。
普通 completed 答复复用既有 completed-command 的投递分工。只有 accepted、没有 agent target、没有 commission adoption 的请求,在原 Turn 确实 completed 时跳过执行准备;prepared、queued、handoff/adoption 都保留原路径。完成状态没有授予发送权限,当前连接、实际原 sender 和 Bot 身份仍由原 Lark 投递路径核验。
Stop 修复位于原 execution-fence 及 holder 一致性检查之后,重新读 canonical Turn;只有 interrupting 且原 holder PID 仍活着时保留原状态。实际 PID 后来死亡仍进入既有恢复,并保留未知效果、不重发输入。没有新增超时、marker、消费者或 schema。取舍依据是 原 Core/FIFO 与回执恢复契约;original-return、current-source、stop-owner 三项落实,production-closure 明确保留后续独立验收。
具体改动 / Changes
四个既有文件增加 119 行、删除 1 行,107 行是测试。生产只增加两个早返回及复用 helper 的导入;没有框架、公共协议或权限扩张。
ChatExternalConversations.recover依据原始 canonical request/Turn,令已完成结果回到原投递责任层;没有创建新执行,也没有把成功状态当来源授权。recover_exited_turn保留活服务正在停止的 Turn。真实 kernel fence、完整本机 holder 身份及锁内重读继续生效;不是只看状态或 released_at 就判断执行已经消失。完成答复测试通过真实临时 Core store、原 transport 和合成 provider,将工作目录/queue preparation 设为会失败,验证不再调用它们。当前来源有效时只回一次原答复;sender 改变、连接撤销或 Bot 不可用时零发送。已保存的下一条输入不因回传而执行;显式断连自身造成的 queued 中断先单独取样,不能误算为 delivery 副作用。Stop 窗口测试使用真实临时执行锁与当前 PID,覆盖 advisory release 成功和写入失败两种情况,断言停止收尾期间的全部原 JSON/JSONL 不变。再使用真实已退出子进程 PID,验证旧服务死亡后的恢复仍可前进。原 App 两个独立会话与群话题 Stop 旅程也通过,避免仅测试新分支的假阳性。
对主干的风险 / Risks
最强反例是跳过执行准备时绕过来源权限,或 Stop 保护把真正死亡的服务也永久保留。最终 124 项相关测试(282 项未选择)覆盖完整答复、三类实际来源拒绝、未执行请求的相反路径、已完成 command 原行为、原 App/group Stop、进程恢复、队列准备和正常激活。模型/provider IO 是合成;文件状态、canonical 生命周期、执行锁与实际 transport 逻辑是真实隔离运行。没有把生产重放当作测试。
相同最终夹具在真实 detached Git 基线 0713a9dd16e91a45b0f9a5549cc0fc16aeb05910 上,四个 completed-answer 用例均在冗余工作目录准备处失败,而 queued 相反用例通过;两种 live-service Stop 用例都因原状态被错误写为 failed 而失败。候选对应行为通过。第一次 Stop 基线命令工作目录错误、导入了候选并通过,已明确标为无效证据,随后核实导入原基线来源并修正;不拿它作基线通过的证明。早期扩展回归还发现上述 Stop 缺陷,修复后才产生本次最终 124 项证据;另一次新夹具使用不存在的 /bin/true 失败,已改为当前 Python 的真实子进程,没有改产品规则来迁就夹具。
最终精确提交标准 premerge 的 6 个直接检查和 2 个选择检查通过,Ruff、diff 检查及四个代码文件/公开 PR 文本边界扫描通过。原有 render_canonical_todo_sections 维护性告警仍保留;真实基线和候选同一命令完整 stdout 相同,没有提高限制。聚合 check 的两个外部 Goal authority 错误保留,代码公开边界扫描本身通过。
这是 default repair,没有 default-off 声明。原 queued 接收、执行/停止权限、agent target/adoption 和原 provider 投递核验保留;readiness 或安装不能授予重放权。当前 Goal 的 wait_for_ci=false,本轮没有获取、查询、轮询或等待 CI。实际服务采用、原受众完整读回和历史业务结果仍须独立验收;测试与合入不等于积压已清空。
我的整体评价 / Verdict
修复复用原生命周期与投递边界,比例适当、可回滚,未引入新的产品步骤。long_horizon 改善体现在已提交结果不再依赖下一次执行准备;user_experience 改善体现在原结果可投递、正常停止不再被误报。两者都有同一最终夹具的故障反例及真实责任层验证。生产采用和最终业务闭环继续保留,不能关闭父任务。
English verdict: APPROVE at 480031a. The existing delivery owner still validates current source authority; live-service Stop ownership is preserved while actual dead-holder recovery remains available. 124 final-source regressions and standard premerge pass. Production adoption and original-audience closure remain unverified.
Completed external replies could wait on workspace or queue preparation before the transport returned their already committed answer. Recovery now leaves these replies to the existing delivery owner, which still revalidates the current connection and original source before sending. Queued input, commission adoption and agent-target recovery retain their existing execution preparation.
Also fix a regression in lost-worker recovery: a requested stop deliberately releases the execution fence before its caller commits the interrupted result. Preserve that live-service stop instead of misclassifying it as a lost execution; recovery after actual service death remains available.
Validation uses the real isolated Core store, original queue and synthetic provider IO: completed answer delivery with unavailable preparation, changed sender, revoked connection and unavailable Bot; queued-input recovery; original stop journeys and live/dead holder counterexamples. Identical final fixtures fail against the frozen Git base. Standard premerge and Ruff pass; the inherited unchanged maintainability advisory remains. Production adoption and original-audience readback are tracked separately.
No new consumer, public schema or permission, and no replay of unknown execution effects. This Goal disables CI waiting; CI was not queried.