Repository navigation
fix(runtime): wait for launchd job removal before service handoff - #8159
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 family; OpenAI; self_reported.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
动机 / Motivation
在 macOS 上升级或恢复已有 LoopX 工作区的用户会遇到这一问题。旧 job 尚未卸载完就加载新服务,切换失败后还可能无法恢复原服务;新路径先核实 job 离场,再继续交接。让原服务管理器安全采用新 Core,并保留活动请求和失败恢复。本次只修服务交接,不代替真实 Bot 消息验收,也不改变账号或请求权限。安装后的原服务采用和历史问题补答仍需独立验收。这次要修的不是模型答复内容,而是已安装的修复无法可靠进入在线服务的一个实际原因。只增加固定休眠不能证明旧服务已经离场,重复加载也会把同一个竞争带入失败恢复。
改动思路 / Approach
这是现有 macOS 服务适配器的 IO 完成条件,留在该 owner 内即可;不新增服务、策略或持久状态。当前 PR 仅等待原 job 移除,并把这一检查接入前向切换、回滚和中断恢复。原来的入口、目标可执行文件资格、实际管理器来源、Chat 准入围栏和私有交接日志继续负责权限与恢复。新增检查仅在原流程已经决定交接之后读取同一标签;返回113才视为已不存在,其他错误或期限到达仍是失败。用表格概括本 head 的差异即可,无需另造状态图:
| 场景 | base | 本 head |
|---|---|---|
| bootout 返回但 job 仍在 | 直接 bootstrap,可能失败 | 先确认 job 移除 |
| 新服务启动失败 | 恢复也会撞上异步卸载 | 同一检查保护恢复 |
| 原服务身份已匹配或存在活动请求 | 复用或拒绝切换 | 保留原行为 |
具体改动 / Changes
关键代码讲解
_wait_unloaded用launchctl print核对原标签,最多等待五秒;未知查询错误不会被当成“服务已不存在”。该函数只观察管理器,不发送第二条停止或业务请求。_align_managed_runtime在已成功 bootout、已记录 stopped 后等待,之后才加载候选;回滚也在恢复原配置和加载前确认离场。这样等待本身失败时仍保留真实已停止服务的恢复责任。_recover_alignment把相同条件用于中断日志恢复,沿原标签、启动参数和数据路由核验,保持一个恢复 owner。
规范依据为 apps/desktop/loopx-control-plane/README.md,固定 revision 54e26effd9cc958179b9fe555d50ba22d13538a6 的服务交接段落。INGRESS:仍先保护全部已接受工作,busy Chat 用例验证无停止/写入。ROUTE:保留注册表、数据、账号和参数,原 retarget/custom-route 用例及真实管理器验证。READBACK:两个 HTTP 服务仍需匹配目标身份,连续三次核验未改变 PID。ROLLBACK:启动失败恢复原配置、中断保留日志并可接续,异步回滚和原中断测试覆盖。以上均为本次映射的规范条目,未把部署后的业务补答算成已验收。
对主干的风险 / Risks
没有发现阻断性问题。最强反例是同步 mock 在 bootout 时立即删 job,使前向和回滚都假绿。本次加入“返回成功但标签再保留两个查询周期”的对抗场景,同一测试在 base 两项失败,在本 head 通过;另有严格未知返回码和期限拒绝。原有135项控制在 base 通过,本 head 三个相邻文件139项通过、4项按原环境条件跳过。
真实 launchd、Core、HTTP 和隔离空工作区测试在构建 Chat bundle、关闭测试上报后于当前 head 另行通过,覆盖原服务恢复、目标采用和三次无重复重启。首次新 checkout 缺少 bundle 的失败记录保留,构建是仓库原验收前提;未以延长期限或删除断言消掉失败。Python 编译、RFC 双语差分和 diff hygiene 通过,两个变更文件的公开边界扫描通过。全局检查另报两个 Goal 的 canonical authority 不可用,作为不属于本次服务交接验证的诊断保留,没有替它们迁移状态。CI 按本 Goal 配置不查询、不等待。
语义与 CI 对齐
这是本机服务适配器的完成条件修复,复用原 activation 错误和恢复日志,没有新协议、指令、账号权限或默认模型策略。五秒确认仍可能保守拒绝极慢卸载;真实原 Bot 的服务采用、旧问题正文和渠道读回不能由隔离测试替代。
我的整体评价 / Verdict
对长期可靠性和用户体验的判断都是 improved:后续正常升级及中断恢复共同使用同一个服务离场检查,避免要求用户反复触发更新。完整变化是20行生产代码与57行新增测试,现有 OS owner 足以承担;未引入兼容分支、持久字段或另一控制器。这个有用的有界修复可以合入,但安装后的原服务身份及逐条业务回复仍开放,不能据此宣称旧消息全部处理完成。
English verdict: APPROVE — head e5acc77; no blocking finding. Native handoff/rollback, base counterfactual and existing controls pass; original Bot delivery remains post-install acceptance.
macOS 的
launchctl bootout可能在旧 job 完全移除前返回。更新器紧接着加载同名新服务会失败,失败后的回滚也会遇到相同竞争。现在由现有 macOS 服务管理适配器有界等待 job 确认移除,再继续原交接;前向切换、回滚和中断恢复共用这一检查。未知查询错误或超时保留失败,原 Chat 准入围栏、活动工作保护、身份校验和恢复日志继续生效。验证:三个相邻 Python 测试文件 139 项通过、4 项按原条件跳过;新增异步卸载场景在原主干上两项失败,在修复后通过。构建 Chat bundle 后另跑真实 launchd、Core、HTTP 和空隔离工作区验证,切换及失败回滚通过,连续三次核验未重复重启服务。源码与测试的公开边界扫描通过;全局检查另报两个既有 Goal 的 authority 未迁移错误,不属于本变更。真实用户的服务采用和旧消息补答须在安装后独立核验。