Skip to content

fix(upgrade): wait for previous App exit before relaunch - #8067

Merged
loopx-agent merged 1 commit into
mainfrom
codex/desktop-upgrade-exit-fence-20261105
Nov 4, 2026
Merged

loopx-agent merged 1 commit into
mainfrom
codex/desktop-upgrade-exit-fence-20261105

Conversation

@loopx-agent

Copy link
Copy Markdown
Collaborator

Goal And Delivered Outcome

正常 loopx upgrade 的 App 重启存在竞态:正常 Quit 返回后,旧 App 仍可能存活。此时立即启动的新进程会被单实例机制结束,旧进程随后退出,原升级请求得不到原生回执,App 也未保留运行。

本变更在同一个正常 Quit 操作内等待目标 App 实际退出,再执行原 LaunchServices 启动。保持已有超时;退出未完成时不启动替代进程,也不伪造升级成功。

  • Outcome basis: 本 PR 自包含的原生升级竞态修复请求。私有运行证据仅保留本地,未公开。
  • Intended base: main,6a92640346051b79d08fb369354180b613c8fa46。

Author Declaration

  • Written by: model_agent,OpenAI Codex。
  • Specification: no written specification; the self-contained request in this PR is the basis.
Criterion Disposition Symbol Evidence
新进程启动前原 App 已退出 implemented _restart_app 真实隔离 AppKit 延迟退出;启动边界独立检查旧 PID 不存在
退出超时不启动 implemented _restart_app 原生超时负例,replacement launcher 未调用
原路径、runtime pin 和原生成功判定保持 implemented 原 restart / _upgrade_desktop 完整升级套件,原 request/channel/identity 回执核验与失败状态保持

Scope And Continuation

仅改变正常 Quit 与原启动之间的时序,仍通过相同路径/argv 选择 App,保留正常退出和既有 native updater。没有 force kill 生产 App、新消费者、权限、receipt schema、签名或降级规则。实际已安装 App 的升级/readback 在正常部署阶段独立验证,fixture 不是整机升级成功证明。

Validation

  • Tested revision: c69b4f97d67dbcb9ec6378738012977a2f3f7aa9。
  • Run state: finished。
  • Input classes: synthetic;authorized_private_read_only 仅用于故障定位。
Check kind Result Public-safe evidence / limitation
real_backend / regression_parity passed 不可变基线加载同一原生测试,旧 PID 仍存活时已调用 replacement launcher,独立断言失败;候选等待完成后通过
integration passed LOOPX_TEST_NATIVE_APP_RESTART=1 python -m pytest -q tests/test_self_upgrade.py:25 项通过,包含 macOS AppKit 的实际异步退出与超时两种情况
static / integration failed 标准 premerge:6 项直接检查通过,8 项 catalog 中 7 项通过;CLI output budget 在未修改基线与候选均缺少相同三个语义字段。该既有失败未修饰为通过,见下文归因
static passed 两文件 Ruff、diff check 与公开边界扫描通过
real_entrypoint not_run 合并前未重复真实升级事务;候选真实正常升级及原生 readback 待部署独验

CLI output budget 的同一命令在不可变基线 6a92640346051b79d08fb369354180b613c8fa46 和候选均失败:interaction_contract、scheduler_hint、selected_todo 缺失。此 PR 的正常 App 退出 helper 不参与这些只读控制面输出,相关文件/调用链未变化。保留原失败;本次变更的必要安装验证(install-local、packaged-install、update smoke)、原生进程退出/超时及全部升级单测独立通过,没有修改预算或删断言。

原生测试显式启用,仅创建独立 AppKit fixture,不使用 LoopX 模型、账户或服务。真实 Quit 与进程退出未 mock;replacement launcher 由只读 oracle 接管以检查旧 PID,并避免启动第二个真实应用。未启用此 macOS 测试时明确 skip,不冒称覆盖。

Frontend / Visual Evidence

UI impact: none;未改变渲染、文案或操作流程。此变更处理同一原生生命周期内的时序,故 first viewport N/A。

Type / Area

  • Bug fix;host/runtime integration;installer lifecycle;native regression test。

Shared-authority RFC fixture impact

N/A;无 Core policy、存储路由、授权或 receipt schema 变化。

Boundary Checklist

  • 无私有会话、凭据、机器路径、日志或原始截图。
  • 原异步失败与真实 upgrade 未完成状态继续保留。
  • 原正常退出、runtime pin、native updater 验证均沿用。
  • 无 RFC pair 变更,exact-head bilingual gate 通过。
  • DCO Signed-off-by。

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; OpenAI Codex; OpenAI; self_reported(host execution attribution unavailable,未声称精确模型构建或人工独立评审)。

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

动机 / Motivation

APPROVE,未发现此 diff 的阻断项。通过正常 upgrade 更新并重启已安装 LoopX App 的用户。旧版正常 Quit 返回后原进程仍存活,新实例提前启动后丢失升级请求;新版等待目标 App 正常退出后才启动,退出超时就不启动替代进程。真实隔离 macOS AppKit 的独立旧 PID 断言证明等待与超时行为;25 项升级检查通过,未把 fixture 当整机升级完成。本 PR 不修改签名、降级、模型、原业务或消息消费责任,不强杀生产进程,也不生成假的原生回执。真实已安装 App 的正常升级与原生成功回执尚待部署独验;父级管家稳定性与历史消息未整体完成。

这不是从代码猜测的等待需求:正常升级的系统日志已显示新实例先结束、旧实例随后退出;同一真实 AppKit 异步退出条件在不可变基线中再次触发“旧 PID 仍活着就启动替代进程”的独立断言。原有 subprocess mock 会抹掉这个时间窗口,因此只靠原单测全绿不足以证明安全。

改动思路 / Approach

在原 Quit 与原 LaunchServices 启动之间等待正常生命周期完成,是最小责任层修复;不需要第二个进程管理器或持久状态。当前边界只增加已有 Quit 操作内的有界退出等待,启动参数、原生 updater、runtime pin、成功与失败回执判定均保留。

仍由原 AppleScript 向预选 App path 发出正常 Quit,等待放在相同的 subprocess timeout 内,每次观察当前应用是否还在运行。原进程未退出就不执行 open;超时将异常交回既有升级失败处理。没有增加 PID 数据库、轮询 daemon、强杀或另一个安装 owner,也没有依靠固定 sleep 宣称 App 已关闭。原 request/channel 和 runtime pin 继续传给同一个 LaunchServices 启动,随后仍由 native updater 核验签名及成功回执。

具体改动 / Changes

关键代码讲解

  1. _restart_app 将原来的 Quit→立即 open 改成 Quit→实际退出→原 open。退出等待复用既有 min(timeout_seconds, 30) 边界,路径仍通过 argv 传入 AppleScript,原 LOOPX_BIN pin 与原生升级参数保持,没有字符串插入路径或更改源选择。
  2. _upgrade_desktop 仍是调用者与回执消费者,未把退出完成升级为成功。它继续要求匹配 request/channel、App 版本、bundled revision、真实活跃 PID 和 runtime identity;没有收到原生终态就保持失败/timeout。此处复核用于确认较小的时序修复没有绕过升级责任层。

产品 +6/-2 行,既有测试模块 +80 行。新增测试显式启用真实隔离 AppKit 进程,其 terminateLater 使 Quit 与退出分开;replacement launcher 只由独立 PID oracle 接管,避免开启第二个应用。成功与超时两臂都运行,没有 mock 正常 Quit 或进程退出。没有单独规定此时序的已接受书面规范,本 PR 自包含缺陷请求为本次修复依据;正常退出、超时不启动、原 pin/receipt 规则三项均有当前实现和检查对应。

对主干的风险 / Risks

主要风险是等待让正常升级挂住,或提前把原 App 杀掉。当前实现保留原超时并只走正常 Quit,超时测试证明 replacement launcher 完全未被调用;退出成功测试在实际 OS PID 已消失后才允许 launch。不可变基线 6a92640346051b79d08fb369354180b613c8fa46 加载同一原生 fixture 时失败,候选 native-enabled 整套 25 项通过。原 23 项 portable 升级、runtime pin、缺失/歧义 App、deferred service 和 native receipt 判定在基线通过,候选也覆盖并通过,输入和持久契约没有迁移。

标准 premerge 的 6 项直接检查及 7 项 catalog 检查通过,包括 install-local、packaged install 和 update smoke。另有一项 CLI output budget FAIL:基线和候选同一命令都缺 interaction_contract、scheduler_hint、selected_todo。已在未修改的 6a92640346051b79d08fb369354180b613c8fa46 独立复现相同失败身份与细节,原生 Quit helper 不参与这条只读控制面输出;未删除断言、放宽预算、选择性重跑成绿色或声称标准 gate 通过。根据当前 review policy,以独立的受影响安装/生命周期检查建立此 diff 的必要验证,保留该既有失败单列继续处理。Ruff、diff hygiene、RFC bilingual 和两文件公开扫描通过。此 Goal 不等待 CI,未查询/轮询 CI。

我的整体评价 / Verdict

在原 Quit 与原 LaunchServices 启动之间等待正常生命周期完成,是最小责任层修复;不需要第二个进程管理器或持久状态。当前边界只增加已有 Quit 操作内的有界退出等待,启动参数、原生 updater、runtime pin、成功与失败回执判定均保留。long_horizon 与 user_experience 均为 improved:原升级请求不会因异步旧实例抢占而无效等待,不增加用户授权或操作回合,超时仍明确失败。已检查 support_control_upgrade.py 与现有桌面 updater 调用路径,没有渲染、文案或交互结构变更;这是同一正常生命周期的时序修复。未验证范围:真实安装后的 native updater/渲染未由本次隔离 fixture 证明;本 PR 没有展示层变化。

保留现有兼容面、原生签名和正常回执判断是合理的,无新增版本分支或状态;生产改动与已证实问题相称。语义检查停在局部 process IO,未创建共享 Core vocabulary。认可这个可回滚增量;合并后必须通过正常 owner 做实际 native 升级/readback,原 CLI budget 失败和父级其余工作继续保留。

English verdict: APPROVE — head c69b4f97d67dbcb9ec6378738012977a2f3f7aa9 waits for actual native App termination before relaunch and preserves timeout/pin/receipt ownership. Native base/head regression, 25 upgrade tests and affected installation checks pass; the independently reproduced pre-existing CLI output failure remains disclosed.

@loopx-agent
loopx-agent merged commit 0423908 into main Nov 4, 2026
19 of 24 checks passed
@loopx-agent
loopx-agent deleted the codex/desktop-upgrade-exit-fence-20261105 branch November 4, 2026 21:18
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