# v7.9.28 热更新交接修复

## 从客户失败日志定位出的阶段
失败日志只有：
- helper started
- source=...

但没有出现：
- client confirmed apply
- stage validation begin/passed
- applying changed payload files

因此下载、解压、回退快照都已经完成，失败发生在“旧客户端把最终替换权交给外部 helper”的阶段，尚未开始复制程序文件。

## 根因
v7.9.25～v7.9.27 的替换器采用“双握手”：
1. helper 启动并写 started marker；
2. GUI 再写 exit-ready marker；
3. helper 等到第二个 marker 后才关闭旧客户端和复制文件。

实际客户环境中 helper 能启动，但第二阶段没有可靠完成；同时 GUI 还会自行执行进程退出。
在 PyInstaller/Windows Job Object、杀毒/安全软件或进程生命周期差异下，这种“helper 等 GUI 信号、GUI 再主动退出”的方式容易造成 helper 在真正复制前被结束，因此黑框一闪即退、日志只留下开头两行。

## v7.9.28 修复
- 完全移除 exit-ready 第二握手。
- helper 一旦成功启动并写 started marker，即表示它已拥有完整、已校验的 stage 和 rollback snapshot。
- helper 自己等待约2秒后，只关闭旧客户端的精确 PID。
- 不再由 GUI 调用 os._exit 强制结束自己，避免 Windows/PyInstaller 进程关系把 helper 一起带走。
- 不使用 taskkill /T，不终止整棵进程树。
- helper 随后独立完成：
  1/5 启动接管
  2/5 校验 stage
  3/5 复制变化文件
  4/5 替换主 EXE
  5/5 提交 VERSION.txt 并重启
- 批处理脚本恢复为纯 ASCII，排除 cmd 代码页/UTF-8 脚本解析差异。
- update_last.log 新增：
  - helper armed
  - taskkill parent return
  - stage validation begin/passed
  - robocopy payload return
  - main executable retry
  - update committed successfully
  后续即使仍失败，也能准确看到卡在哪一步。

## 重要：v7.9.25 的一次性人工过渡
热更新器本身运行在“当前旧版本”里。
因此 v7.9.25 客户端尝试升级到任何新版本时，仍然执行 v7.9.25 自己的旧交接代码。

也就是说：已经卡在 v7.9.25 的客户端无法通过下载一个“修好的目标包”来修复它正在运行的旧更新器。
需要先人工覆盖/安装一次 v7.9.28。
从 v7.9.28 开始，后续版本再通过热更新验证新流程。
