68 lines
7.3 KiB
Markdown
68 lines
7.3 KiB
Markdown
---
|
||
id: T-403
|
||
title: SoftBox 自更新助手与健康确认恢复
|
||
phase: 4
|
||
deps: [T-402]
|
||
status: TODO
|
||
created: 2026-07-19
|
||
issue: null
|
||
context_ref: null
|
||
claim_branch: null
|
||
work_branch: null
|
||
write_paths:
|
||
- docs/tasks/T-403.md
|
||
- core/updater/
|
||
- app-modern/platform/windows/
|
||
- app-win7/platform/windows/
|
||
- app-modern/cmd/softbox/
|
||
- app-win7/cmd/softbox/
|
||
- app-modern/cmd/softboxupdater/
|
||
- app-win7/cmd/softboxupdater/
|
||
- scripts/verify_phase0.ps1
|
||
- scripts/verify_phase0.sh
|
||
- docs/api.md
|
||
- docs/03-tech-stack.md
|
||
- docs/04-architecture.md
|
||
- docs/00-ai-start-here.md
|
||
- docs/current-state.md
|
||
---
|
||
|
||
## 问题 / 背景
|
||
|
||
Phase 4 的子软件更新已能在用户关闭子软件后安全委托既有安装事务,但 SoftBox 主程序自身不能在运行中替换 EXE。当前架构/API 只描述了 `SoftBoxUpdater.exe --pid --staging --target` 的目标形状,仓库没有独立 updater、主进程退出等待、受限自更新目录布局、健康确认或中断恢复。因此下载到 staging 的新版无法安全激活,也不能在新版未正常启动时可靠保留旧版恢复路径。
|
||
|
||
自更新更新的是盒子 `app/`,不是 `apps/<id>/`。若助手接受任意 target/staging、跟随 symlink,或把“子进程已创建”误当成健康,可能覆盖 `data/`、`licenses/` 或把不可用新版永久设为 current。当前没有可信自更新包下载/签名消费源,本任务只能提供独立、受约束的本地切换与恢复机制,不能伪称在线自更新已经接通。
|
||
|
||
## 方案
|
||
|
||
1. 新建 Go 1.20 兼容的 `core/updater`,固定布局为 `<root>/app`(target)、`<root>/staging/<request-id>`(已由可信外层准备的新目录)、`<root>/backups/<request-id>`、`self-update-transaction.json` 与健康确认文件。请求只接受正主进程 PID、safe request ID、绝对且严格满足该布局的 staging/target;逐项 `Lstat` 拒绝 symlink、非目录、交叉 root、预存 backup/journal 异常,绝不接受 UI/CLI 传入可执行参数或任意移动路径。
|
||
2. updater 先恢复上次未完成 transaction,再等待原主 PID 自然退出;取消、超时和等待错误均不触碰 target。退出后按 journal 栅栏执行 `prepared → target_backed_up → staging_activated → launched → committed`:只可把 old `app` 改名为本 request 的 backup、把同 root staging 改名为 `app`;每步用临时 JSON + 原子替换并同步目录。任何启动前/启动失败路径尝试恢复 old app;无法恢复时保留可验证 journal/backup,下一次 updater 可恢复,绝不删除 data/licenses。
|
||
3. 新版只能以已验证绝对 `<root>/app/SoftBox.exe`、固定工作目录和内部固定 `--softbox-update-health <request-id>` 参数启动。`SoftBox.exe` 在 Gio Layout 之前(非 UI goroutine I/O)从自己的可执行路径推导同一 root,校验 request ID 和 health locator 后原子写入仅含 request ID 的健康确认;不得接受路径、URL 或任意命令。助手等待匹配确认后才 committed/清理 backup;超时、错误或错误 request ID 均返回稳定失败,保留恢复材料。新版已运行但未确认且 Windows 拒绝 rollback 时 fail closed 留 journal,不强杀;后续受控 updater 在主 PID 退出后恢复。
|
||
4. 两端 `platform/windows` 增加一致的主 PID 自然退出和固定内部健康启动边界。Windows 以 `OpenProcess(SYNCHRONIZE)` + 可取消的短 `WaitForSingleObject` 轮询实现,不将无权限/不存在 PID 当作“已退出”;启动不经 shell/PATH、只传固定 health flag。非 Windows stub 明确返回不支持。clock/process/launcher seam 覆盖已退出、正常退出、取消、超时、OpenProcess/wait 错误与参数固定性。
|
||
5. 两端新增无 Gio 的 `cmd/softboxupdater`,严格解析 `--pid`、`--staging`、`--target`(内部 request ID 由助手生成),装配 core/platform 后以非 0 退出失败;主 cmd 只识别内部 health flag 并继续正常启动,不增加用户可传的任意执行入口。验证脚本同时构建主 EXE 和 Updater EXE;文档定义内部健康文件、恢复语义、真实下载/签名未装配事实与 T-601 真机限制。
|
||
|
||
## 验收要点
|
||
|
||
- core updater 拒绝非法 PID/request ID、相对/跨 root/不匹配布局、symlink/非目录、缺 staging、已有冲突 backup/journal;失败不改 target、staging、data 或 licenses。成功路径等待原 PID 退出,正确 rename/sync/journal,固定启动新 app,收到匹配 health request ID 后才 committed 并清理 backup/journal。
|
||
- PID 等待的取消、超时、OpenProcess/Wait 错误、launcher 失败、health 超时/错误 ID、每个 rename/journal 故障和崩溃注入均有稳定错误;可恢复时 old app 恢复且可启动,无法立即恢复时 journal/backup 保留供下次 `Recover`,不强杀新/旧进程。
|
||
- 新主程序只响应固定内部 health flag,从自身 `app/SoftBox.exe` 推导 root 并原子写入最小确认,不在 Gio Layout I/O;不匹配 flag/request/layout 不写文件。双端 `SoftBoxUpdater.exe` CLI 拒绝多余/无效参数,使用平台 PID 等待和固定启动调用。
|
||
- core 无 Windows/Gio/第三方依赖;双端平台 public contract 与 non-Windows stub 一致;Windows amd64 交叉编译主程序和 Updater,脚本/CI 闸门覆盖两个 EXE。真实 Windows 文件锁、杀毒/断电以及 UAC/签名发布行为仍由 T-601/T-603 验证。
|
||
- `go -C core vet ./...`、`go -C core test -count=1 ./...`、`go -C core test -count=10 ./updater`、两个主程序和 Updater 的 Windows amd64 构建、`./scripts/verify_phase0.ps1`、`python scripts/validate_agent_context.py`、`python scripts/validate_harness_governance.py` 全部通过;执行记录如实写明未装配可信自更新包下载/签名来源。
|
||
|
||
## 边界(不改什么)
|
||
|
||
- 不实现自更新 Catalog/package 下载、Ed25519 签名、SHA-256 校验、版本检查或自动触发;staging 只可由后续可信外层提供,不能把任意下载文件解压/执行。
|
||
- 不触碰 `apps/<id>/` 子软件安装事务、`data/`、`licenses/`、子软件更新、授权策略或 allow-all 注入;不实现 updater 自更新、MSI、服务、注册表、shell script、强杀、命名管道或 UI 进度窗口。
|
||
- 不把 Windows API/Gio/SQLite 引入 core,不升级 Go/Gio/依赖,不开放 `SoftBox.exe` 的用户自定义 path/args/command;内部 health flag 仅由 updater 固定生成。
|
||
- 不把单元测试等同于断电、文件锁、杀毒和 Windows VM/真机结论;这些保留 T-601/T-603。
|
||
|
||
## 协作约束
|
||
|
||
- 当前项目为单 Agent 串行模式;当前 Agent 独占全部 `write_paths`,自行完成规格、实现、审查、测试和提交,不启动子 Agent。
|
||
- 先提交本任务规格和项目快照;领取后将本文件改为 `DOING`,填写当前 HEAD 的 `context_ref` 与 `work_branch: agent/codex/T-403`,再重新记录基线和实现。
|
||
- 本任务完成后停止;后续 Phase 5 许可证任务仍需按路线图另行正式落成,不能因自更新程序存在而假定下载、签名或发布流水线已完成。
|
||
|
||
## 执行记录
|
||
|
||
- 2026-07-19:正式落成。以 T-402 完成后的 Phase 4 依赖为基线,冻结独立 updater、受限根目录、PID 自然退出、健康确认 journal 和可恢复 rollback 的最小自更新协议;明确可信 self-update 下载/签名、真机故障和发布签名继续后置。
|