Files
soft_quay/docs/tasks/T-617.md
T

63 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: T-617
title: 自更新事务恢复与健康门禁加固
phase: 4
deps: [T-403]
status: TODO
created: 2026-07-19
issue: null
context_ref: null
claim_branch: null
work_branch: null
write_paths:
- docs/tasks/T-617.md
- core/updater/updater.go
- core/updater/recovery.go
- core/updater/updater_test.go
- app-modern/cmd/softbox/update_health_test.go
- app-win7/cmd/softbox/update_health_test.go
- docs/review/phase4-review.md
- docs/api.md
- docs/04-architecture.md
- docs/06-tasks.md
- docs/00-ai-start-here.md
- docs/current-state.md
---
## 问题 / 背景
`docs/review/phase4-review.md` 的 O1/O4 确认 T-403 的自更新状态机保留了 backup、从不强杀进程,Windows 真机锁/杀毒/断电仍由 T-601 验证;但无头自动测试未覆盖 journal、rename、目录同步、清理失败和 `staging_activated`/`launched`/`committed` 的中断恢复,也没有双端主程序内部 health flag 的参数拒绝测试。
进一步复核发现一个必须修复的恢复一致性问题:`prepared` transaction 写入后,`current(app) → backups/<request-id>` 的 rename 可能已成功,但其目录同步失败。此时当前代码返回 `failBeforeActivation`,磁盘实际为“target 缺失、backup 存在、phase=prepared”;下次 `recoverLayout` 却把 `prepared` 一律当作未移动而只删除 journal,旧 app 不会恢复。这是可用性/恢复缺陷,不能仅靠 T-601 真机验证或测试补充掩盖。
## 方案
1. 收紧 `core/updater` 的 `prepared` 恢复语义:只根据受限布局内经 `Lstat` 验证的 target/backup 实际状态决定收敛。target 存在且 backup 不存在时才可删除 prepared journal;target 缺失且 backup 存在时必须先恢复 backup;任何其他组合 fail closed 并保留 journal/材料。首次 backup rename 返回错误时,`Update` 必须尝试同一受限恢复路径;恢复不能安全完成时保留原始错误、`ErrRecoveryRequired`、journal 与 backup,绝不删除 data/licenses 或强杀进程。
2. 扩展 `core/updater/updater_test.go`,优先复用既有 `DirectorySyncer` 注入点和受控临时目录,不为测试引入通用生产文件系统抽象。覆盖 backup rename 后 sync 失败的即时/下次恢复、健康超时后 rollback 被结构性阻塞并在条件解除后 Recover、prepared/target_backed_up/staging_activated/launched/committed 的中断状态、transaction 写入/目录同步/cleanup 失败的错误链和可恢复性。对无法在无头环境稳定模拟的 Windows EXE 锁,只验证相同 fail-closed 状态机,不宣称替代 T-601。
3. 两个 `cmd/softbox` 新增镜像的 `update_health_test.go`,验证非内部 flag 不被消费、health flag 缺 request ID 或含多余参数时返回已处理错误;不伪造 `os.Executable`、目录 sync 或成功 health 确认。
4. 更新 Phase 4 审核结论、架构/API 的自更新恢复表述和项目快照:明确 prepared rename+sync 不一致已由本任务修复;保持“可信自更新包下载/验签/版本选择/UI 触发、真实 Windows 故障注入尚未装配”的边界。
## 验收要点
- 任何 `prepared` journal 都不会在 target 缺失且 managed backup 存在时被直接删除;成功恢复后旧 `app/SoftBox.exe` 回到 target,失败时保留 journal/backup 并让 `errors.Is(err, ErrRecoveryRequired)` 成立。backup rename 后 directory sync 失败不再使旧 app 失去受控恢复路径。
- 对 prepared、target_backed_up、staging_activated、launched、committed 五个可持久化 phase,测试构造合法受限布局并证明 Recover 只会恢复旧 app 或完成已确认的新 app;不安全/矛盾布局 fail closed,不触碰 `data/`、`licenses/` 或受管根外路径。
- health timeout 后 rollback 被阻塞时,返回同时保留 health 原因与恢复原因,保留 journal/backup;解除阻塞并再次 Recover 后状态可预测收敛。transaction/rename/sync/cleanup 的可注入失败均保留可检查错误链;不能在无头测试稳定触发的 Windows 文件锁仅记录为 T-601 真机项。
- modern 与 Win7 的内部 health CLI 参数拒绝测试一致;Gio Layout 无 I/O,core 继续保持 Go 1.20 且不 import Gio、Windows API 或第三方文件系统抽象。
- `go -C core vet ./...`、`go -C core test -count=10 ./updater`、`go -C core test -count=1 ./...`、两个 app 的 `cmd/softbox` 全包测试、双端 Windows amd64 主程序/Updater 构建、`./scripts/verify_phase0.ps1`、`python scripts/validate_agent_context.py`、`python scripts/validate_harness_governance.py` 与 `git diff --check` 全部通过。真实 Windows 锁、杀毒和断电不在本任务完成声明中。
## 边界(不改什么)
- 不实现可信自更新 Catalog/package 下载、签名、SHA-256、版本选择、自动触发、Updater UI、命名管道、强杀、MSI、服务或注册表;不把未验证 staging 变成可执行来源。
- 不把 Windows 文件锁、杀毒软件、UAC/签名发布或物理断电的真机结论移入单元测试;它们仍归 T-601/T-603。
- 不改子软件 install/update 事务、Catalog/下载/授权协议、Gio/Go 工具链、data/licenses 语义或平台公开接口;不为方便失败注入引入泛化生产文件系统 mock 层。
## 协作约束
- 当前项目为单 Agent 串行模式;当前 Agent 独占全部 `write_paths`,自行完成规格、恢复状态机审查、测试、验证和提交,不启动子 Agent。
- 先提交本任务规格及路线图/快照/审核修正;领取后将本文件改为 `DOING`,填写当前 HEAD `context_ref` 与 `work_branch: agent/codex/T-617`,再运行基线和实现。
- T-501 仅在本任务 `DONE`、完整验证和实现提交后正式落成;不得把 O2/O5 的可信 Catalog、下载消费、授权或发布源装配夹带入本任务。
## 执行记录
- 2026-07-19:正式落成。根据 Phase 4 审核 O1/O4 及补充状态机复核,冻结“prepared rename 后 sync 失败必须恢复/保留、五 phase Recover、故障错误链与双端 health flag 参数拒绝”的最小整改闭环;真实 Windows 锁与发布侧装配继续后置。