Define self-update recovery remediation (T-617)
This commit is contained in:
@@ -47,7 +47,7 @@ SoftBox 软件盒子是一个使用 Go + Gio 开发的 Windows 桌面客户端,
|
||||
|
||||
## 当前阶段
|
||||
|
||||
当前项目已完成 Phase 0~2、T-301~T-303、T-615 与审核整改 `T-604`~`T-614`。Windows 安全路径阻断项、图标缓存资源边界、后台结果回 UI 线程的事件接线、双适配器交互契约、`VisibleItems` 快照生命周期、双端 Gio shell 职责拆分、unsafe cache 安全诊断/runbook、ZIP 中央目录/EOCD(含 ZIP64)预扫描、安装文件/目录/journal 的代码层耐久顺序、Catalog canonicalization/签名静态 corpus,以及同句柄 Catalog size/SHA→严格 app.json→staging/switch/回滚安装链均已关闭;T-303 已将 verified-package 的 staging 前磁盘/运行状态预检和稳定失败码落实到 core,T-615 已将 ZIP 输入/staging 输出 I/O 分界、平台磁盘满分类和清理失败落实到 core。T-401 已完成完整路径进程检测、受控启动和 Switcher 临界区复查;T-402 已完成子软件更新编排;T-403 已完成受限 SoftBoxUpdater、自身 EXE 健康确认与可恢复切换;T-616 已完成 production cmd 的 Catalog 快照事件投递和明确空态诊断。物理断电、文件锁与杀毒软件干扰验证保留到 T-601 发布前环境验证。
|
||||
当前项目已完成 Phase 0~2、T-301~T-303、T-615 与审核整改 `T-604`~`T-614`。Windows 安全路径阻断项、图标缓存资源边界、后台结果回 UI 线程的事件接线、双适配器交互契约、`VisibleItems` 快照生命周期、双端 Gio shell 职责拆分、unsafe cache 安全诊断/runbook、ZIP 中央目录/EOCD(含 ZIP64)预扫描、安装文件/目录/journal 的代码层耐久顺序、Catalog canonicalization/签名静态 corpus,以及同句柄 Catalog size/SHA→严格 app.json→staging/switch/回滚安装链均已关闭;T-303 已将 verified-package 的 staging 前磁盘/运行状态预检和稳定失败码落实到 core,T-615 已将 ZIP 输入/staging 输出 I/O 分界、平台磁盘满分类和清理失败落实到 core。T-401 已完成完整路径进程检测、受控启动和 Switcher 临界区复查;T-402 已完成子软件更新编排;T-403 已完成受限 SoftBoxUpdater、自身 EXE 健康确认与可恢复切换;T-616 已完成 production cmd 的 Catalog 快照事件投递和明确空态诊断。T-617 已正式落成,用于修复 Phase 4 审核发现的 prepared rename+sync 恢复不一致并补足自更新恢复测试。物理断电、文件锁与杀毒软件干扰验证保留到 T-601 发布前环境验证。
|
||||
|
||||
优先路径:
|
||||
|
||||
@@ -55,7 +55,7 @@ SoftBox 软件盒子是一个使用 Go + Gio 开发的 Windows 桌面客户端,
|
||||
2. 已完成 Phase 1:清单验签、ZIP 安全解压、原子切换回滚原型。
|
||||
3. 已完成 Phase 2 与 T-301:清单/列表/详情/图标缓存 + 可恢复下载队列。
|
||||
4. 已完成 T-604:modern/Win7 workspace 与 Gio 版本解析彻底隔离。
|
||||
5. 已完成 T-606~T-615:图标缓存资源边界、UI 线程事件接线、双 Gio 适配器交互契约、`VisibleItems` generation 生命周期、双端 `shell.go` 同 package 镜像职责拆分、unsafe cache 诊断/人工恢复指引、ZIP 中央目录/EOCD 预扫描、安装耐久顺序、Catalog 静态签名向量,以及 staging 输出 I/O 根因与磁盘满诊断;已完成 T-302/T-303:已验签 Catalog 选择与同句柄 size/SHA、严格 app.json、安全 staging/switch/健康与记录写回滚链路,以及 staging 前磁盘/运行状态预检与稳定失败码。T-401 已完成进程检测、受控启动与切换临界区复查;T-402 已完成关闭确认、自然退出等待和更新编排;T-403 已完成 SoftBoxUpdater 的受限 transaction、PID 自然退出、固定健康启动与恢复;T-616 已完成 Catalog 快照启动投递与明确空态诊断。下一步按路线图正式落成 Phase 5 的 T-501。T-601 仍须补真实 Windows 环境的断电/干扰注入。
|
||||
5. 已完成 T-606~T-615:图标缓存资源边界、UI 线程事件接线、双 Gio 适配器交互契约、`VisibleItems` generation 生命周期、双端 `shell.go` 同 package 镜像职责拆分、unsafe cache 诊断/人工恢复指引、ZIP 中央目录/EOCD 预扫描、安装耐久顺序、Catalog 静态签名向量,以及 staging 输出 I/O 根因与磁盘满诊断;已完成 T-302/T-303:已验签 Catalog 选择与同句柄 size/SHA、严格 app.json、安全 staging/switch/健康与记录写回滚链路,以及 staging 前磁盘/运行状态预检与稳定失败码。T-401 已完成进程检测、受控启动与切换临界区复查;T-402 已完成关闭确认、自然退出等待和更新编排;T-403 已完成 SoftBoxUpdater 的受限 transaction、PID 自然退出、固定健康启动与恢复;T-616 已完成 Catalog 快照启动投递与明确空态诊断。T-617 已正式落成,当前执行自更新恢复状态机修复与测试整改;完成后再按路线图正式落成 Phase 5 的 T-501。T-601 仍须补真实 Windows 环境的断电/干扰注入。
|
||||
|
||||
## 领取任务规则
|
||||
|
||||
|
||||
@@ -105,6 +105,14 @@ Phase 3 的 T-301 → T-302 → T-303 是安全关键依赖链,任务之间保
|
||||
| T-402 | 子软件更新流程 | T-401 | 可更新识别→确认退出→更新→失败回滚仍可启动旧版;更新不触碰 data/licenses |
|
||||
| T-403 | SoftBoxUpdater 盒子自更新 | T-402 | 独立 Updater 按 CLI 合约工作;更新中断可恢复;新版健康状态写入 |
|
||||
|
||||
#### Phase 4 交叉复核整改
|
||||
|
||||
T-617 先关闭 T-403 自更新的 `prepared` rename 后目录 sync 失败会留下 target 缺失/backup 存在而旧 Recover 删除 journal 的恢复一致性缺陷,并补齐可无头验证的故障注入、五个 transaction phase Recover 与双端 health CLI 参数拒绝测试。Windows 文件锁、杀毒和断电真机结论仍归 T-601;可信 Catalog/下载/许可证/自更新发布源装配另行拆任务。
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 |
|
||||
| --- | --- | --- | --- |
|
||||
| T-617 | 自更新事务恢复与健康门禁加固 | T-403 | prepared rename+sync 不一致可恢复或保留 journal/backup fail closed;五 phase 恢复、可注入 durability/cleanup 故障链和双端 health flag 参数拒绝均有证据 |
|
||||
|
||||
### Phase 5 · 授权
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 |
|
||||
|
||||
@@ -13,12 +13,13 @@
|
||||
## 当前快照
|
||||
|
||||
- 日期:2026-07-19
|
||||
- 阶段:Phase 2 已完成(T-201~T-204)并由 T-616 补齐启动 Catalog 快照投递/空态诊断;Phase 3 的 T-301 可恢复下载队列、T-302 安装流程整合、T-303 失败处理/磁盘预检查与 T-615 staging 输出 I/O/磁盘满诊断整改已完成;审核整改 T-604~T-614 与 Phase 4 的 T-401 进程检测、受控启动和切换临界区复查、T-402 子软件更新编排、T-403 受限盒子自更新事务已完成
|
||||
- 阶段:Phase 2 已完成(T-201~T-204)并由 T-616 补齐启动 Catalog 快照投递/空态诊断;Phase 3 的 T-301 可恢复下载队列、T-302 安装流程整合、T-303 失败处理/磁盘预检查与 T-615 staging 输出 I/O/磁盘满诊断整改已完成;审核整改 T-604~T-614 与 Phase 4 的 T-401 进程检测、受控启动和切换临界区复查、T-402 子软件更新编排、T-403 受限盒子自更新事务已完成;T-617 已正式落成,正在整改 prepared rename+sync 恢复不一致与自更新测试覆盖
|
||||
- 技术栈:根 Go 1.25 workspace 只纳入 core/app-modern,`app-win7/go.work` 独立纳入 core/app-win7;版本闸门证明 modern Gio v0.10.1 与 win7 Gio v0.6.0 不交叉解析
|
||||
- 生产代码:core 已有 Catalog/本地状态/存储、共享 Windows 安全相对路径策略与静态跨实现 canonicalization/Ed25519 vector corpus(拒绝非法 surrogate、`-0` 和非唯一 Base64 signature,大整数保持 token)、安全 ZIP 解压/回滚原型及 T-302/T-303/T-615 安装 use case(`core/application/install.InstallService` 只取已过滤 Catalog entry + architecture,强制注入 disk/storage-failure/target-state checker;`Extractor.ExtractVerifiedFileWithCheck` 在同一普通文件句柄按 size→SHA-256→EOCD/ZIP64→严格 app.json→已规划 payload 的 staging 前预检→安全 staging 的顺序处理,空间要求为 payload+64 MiB,ZIP 输入错误与 staging 创建/write/sync/close 错误分界并保留原始 I/O 链;平台可识别的后者磁盘满返回 `disk_full`,其余输出 I/O 返回稳定 code 且不触发 switch;清理失败可观察,Recover 仅删除已验证 layout 内的残留 staging;每个实际 payload 文件 hash 与受验证 entrypoint/working directory/min_os/requires_admin 写入 installed-app;health 或记录写失败经 Switcher 回滚;更新 current→backup 紧邻前复查精确 entrypoint,明确运行/检测故障保持旧版本并清理 staging),transaction/switch/rollback/recovery 的 journal、rename、清理经统一 fail-closed 耐久栅栏,Windows 使用目录句柄 FlushFileBuffers)、纯 core `application/launch`(只接收 app ID、受控 current/普通 entrypoint/兼容/授权/运行状态/启动器接口全部 fail closed)、双端 Toolhelp 完整映像路径检测/Win7 可用系统版本判断/无参数受控启动与非 Windows fail-closed stub、发布稳定只读 generation 的无 IO 软件列表模型、按 key in-flight + 流式有界读取 + 32 MiB/256-key LRU 的可信图标缓存、图标 Load/Decode 事件发布用例、有界 application event relay,以及默认并发 2 的持久可恢复下载队列;modern/win7 主循环已接 relay/Invalidate,AppShell 已实现搜索/分类/视图、惰性列表、详情右栏、完整图标失败 identity 生命周期与仅 `unsafe_cache` 可见的安全 locator/人工恢复提示,并按 root/header/catalog/detail/style 同 package 镜像职责拆文件
|
||||
- T-402 更新用例:`core/application/update` 只接收外层可信的 install selection,验证已装版本/旧 entrypoint后,运行中才请求关闭确认并以 1 秒~10 分钟上限等待自然退出,随后委托已有 `InstallService` 的双重运行复查与 rollback;取消、超时、Toolhelp 检测错误和安装错误均保留稳定 code/错误链,不强杀且不改 `data/`、`licenses/`。两端 platform 对齐 `WaitForExit` 契约,Windows 固定短轮询完整路径,非 Windows 返回明确不支持。
|
||||
- T-403 自更新:`core/updater` 只接受正 PID、真实 `<root>/app` 与同 root `<root>/staging/<safe-request-id>`,先等待旧 PID 自然退出,随后恢复遗留 journal 或以 `prepared → target_backed_up → staging_activated → launched → committed` 切换;目录 rename、原子 JSON 和目录 durability 均由受限路径与平台同步栅栏保护。助手只启动固定 `<root>/app/SoftBox.exe --softbox-update-health <request-id>`,主程序在 Gio Layout 前从自身 EXE 写最小 health 确认;失败优先恢复旧 app,Windows 锁阻止恢复时保留 backup/journal,不强杀。两端有无 Gio `cmd/softboxupdater`,非 Windows 平台边界 fail closed;没有自更新下载、签名消费、版本选择或 UI 触发器。
|
||||
- T-616 启动 Catalog:`core/application.CatalogBootstrap` 只消费 composition 注入的已验证、目标过滤内存快照,以深拷贝 `CatalogRefreshed`/稳定 `CatalogRejected` payload 经现有 runtime/relay 交给 UI Frame;双端 shell 在 UI goroutine 更新 `SetItems` 并明确显示 loading、未配置、加载失败、已加载空目录与筛选空结果。当前 cmd 有意注入 `UnconfiguredCatalogLoader`,故 `dist/SoftBox.exe` 不会显示 fake 软件,而会显示“Catalog 来源尚未配置”;可信 URL/公钥/缓存 composition 仍待发布配置。
|
||||
- T-617 自更新整改(执行中):Phase 4 复核发现 `prepared` journal 写入后,`app → backups/<request-id>` rename 已成功但目录 sync 失败时,旧恢复逻辑会删除 journal 而不恢复 backup。T-617 必须让实际 target/backup 拓扑驱动受限恢复,并补 durability/cleanup 失败、五 phase Recover 与双端 health flag 参数拒绝测试;Windows 真机锁、杀毒和断电仍待 T-601。
|
||||
- 测试:core 覆盖 Catalog 静态 canonicalization/Ed25519 vectors、非法 surrogate/`-0`/Base64 fail-closed、列表快照 generation/零复制、SemVer/12 状态、本地安装记录、Windows dot-space/设备名/Unicode 折叠路径攻击、ZIP destination 包含性与 EOCD/ZIP64 原始包/中央目录/条目数预扫描、T-302/T-303/T-615 同句柄 package size/SHA、严格/有界 app.json、verified payload 预检 hook、容量精确阈值/故障、程序运行/状态故障、稳定安装失败码、staging write/sync/close ENOSPC 与普通输出 I/O 原因保留、CRC 输入分界、清理失败/受控恢复、payload hash 与启动元数据记录、Catalog 选择拒绝、transaction recovery、health/记录写失败回滚、switch 临界区复查、受控启动的旧 metadata/unsafe layout/缺文件/兼容/授权/运行/启动失败、payload/staging tree/journal/rename/rollback/recovery/cleanup 耐久顺序及错误注入、Windows 原生目录 `FlushFileBuffers`、图标并发/取消/读取边界/LRU、真实目录/symlink fail-closed 与 cache→`unsafe_cache` event、relay 背压与关闭、下载并发/暂停/取消/重试/Range/断连/恢复/事件失败与文件身份替换;两个 app 覆盖 Toolhelp snapshot full-path collision/error seam、OS version 判断和非 Windows fail-closed stub,以及 Editor/视图/分类/行/恢复/关闭接线、500 项 viewport、AppID 控件与分类控件生命周期、详情上下文、空状态语义、UI drain 前后、图标失败身份生命周期与 `unsafe_cache` 详情语义;安装恢复矩阵保持通过
|
||||
- 数据:`schemas/` 已有 manifest/app.json/installed-app.json/download-task.json v1 Schema并注明 Windows 路径运行时权威规则;`testdata/catalog/` 有公开虚构清单样例和 v1 静态 canonicalization/Ed25519 corpus;`testdata/zip/` 与 `testdata/download/` 记录运行时生成的攻击/传输矩阵
|
||||
- 标准启动路径:`./init.sh` / `./init.ps1`(同步依赖、执行完整 Phase 0 闸门、打印双目标构建命令)
|
||||
@@ -31,7 +32,7 @@
|
||||
| 路径 | 状态 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `docs/` | 已有 | harness coding 文档集(本次初始化完成) |
|
||||
| `docs/tasks/` | 已有 | Phase 0~2、T-301~T-303、T-604~T-616、T-401~T-403 已完成 |
|
||||
| `docs/tasks/` | 已有 | Phase 0~2、T-301~T-303、T-604~T-616、T-401~T-403 已完成;T-617 正在执行 |
|
||||
| `scripts/` | 已有 | harness 治理、core 边界、Go 版本检查与 Phase 0 双平台验证入口 |
|
||||
| `core/` | 已建 | Go 1.20 兼容;已有正式 Catalog、本地状态/存储、共享 Windows safepath、列表模型、有界并发图标缓存、图标事件/relay、可恢复下载队列与 Phase 1 安装安全原型 |
|
||||
| `app-modern/` | 已建 | Go 1.25.0 + Gio v0.10.1;Modern AppShell 已接入虚拟列表、详情、图标事件 drain/过期拒绝和内存 ImageOp,并拆为五类 shell 职责文件 |
|
||||
@@ -44,7 +45,7 @@
|
||||
任务状态以 `docs/tasks/` 各任务文件 frontmatter 的 `status` 为准。本节只写项目级摘要:
|
||||
|
||||
- 已完成:Phase 0 的 `T-001`~`T-004`;Phase 1 的 `T-101`、`T-102`、`T-103`;Phase 2 的 `T-201`~`T-204` 与 `T-616`;Phase 3 的 `T-301`~`T-303` 与 `T-615`;审核整改 `T-604`~`T-614`;Phase 4 的 `T-401`~`T-403`。
|
||||
- 正在进行:无;下一步可按路线图正式落成 Phase 5 的 T-501。T-601 的物理断电与干扰故障注入仍保留为发布前环境验证。
|
||||
- 正在进行:T-617(依赖 T-403 已完成);完成、验证并提交后,再按路线图正式落成 Phase 5 的 T-501。T-601 的物理断电与干扰故障注入仍保留为发布前环境验证。
|
||||
|
||||
## 当前可运行内容
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
## 总体判断
|
||||
|
||||
**Phase 4 的实现设计质量高,未发现可证实的命令注入、强杀或越界移动安全缺陷。** 三个最敏感的点都做对了:**启动无命令注入面**(调用方给不了路径/参数/工作目录,平台层无 shell、无用户参数、绝对路径)、**更新绝不强杀**(只请求关闭 + 等自然退出)、**自更新可回滚且健康确认前不删 backup**。同时 **Phase 3 审查的 O1(切换前不复查运行状态)已被 T-401 闭合**。
|
||||
**Phase 4 的启动/更新安全设计质量高,未发现可证实的命令注入、强杀或越界移动安全缺陷;自更新恢复状态机存在一项须由 T-617 修复的 durability 后状态不一致。** 三个最敏感的点都做对了:**启动无命令注入面**(调用方给不了路径/参数/工作目录,平台层无 shell、无用户参数、绝对路径)、**更新绝不强杀**(只请求关闭 + 等自然退出)、**自更新健康确认前不删 backup**。同时 **Phase 3 审查的 O1(切换前不复查运行状态)已被 T-401 闭合**。
|
||||
|
||||
不过,补充全栈复核发现 T-403 的自动化故障覆盖低于任务验收所要求的范围。因此 O1 不能仅视为普通“优化项”:Windows 真机锁语义仍属于 T-601,但可在无头环境验证的失败注入、崩溃 phase 和恢复契约应作为后续整改的高优先级测试项。O2、O3 仍是准确的架构/装配边界说明。
|
||||
|
||||
@@ -80,6 +80,8 @@
|
||||
|
||||
**建议:**单列整改任务,先为 updater 的文件操作和目录同步提供最小失败注入 seam,补齐健康超时→rollback 被锁阻塞→后续 `Recover`、各 transaction phase 崩溃恢复、journal/rename/sync/cleanup 失败与 health CLI 参数拒绝的双端测试。真实 Windows 文件锁、杀毒软件和断电结果仍只由 T-601 真机/VM 验证,不以单元测试替代。
|
||||
|
||||
**追加状态机发现:**`prepared` journal 成功写入后,`app → backups/<request-id>` 的 `os.Rename` 可能已经完成,而 `renameDirectory` 在随后的目录 sync 返回错误。当前 `Update` 直接返回 `failBeforeActivation`;磁盘成为 target 缺失、managed backup 存在、journal 仍为 `prepared`。现有 `recoverLayout` 对 `prepared` 无条件删除 journal,因而不会把 old app 恢复到 target。该情形不是 Windows 真机锁的未知行为,而是可由既有 `DirectorySyncer` seam 在无头测试复现的恢复一致性缺陷。T-617 必须修复此拓扑判断并覆盖即时/下次恢复;若恢复仍失败,必须保留 journal/backup 与 `ErrRecoveryRequired`。
|
||||
|
||||
### O5 · 端到端装配依赖应拆开跟踪
|
||||
|
||||
**现状:**O2 正确指出 launch/update/updater 目前只完成无头 core 边界;production cmd 尚未装配可信 Catalog、许可证或 completed-download 消费源。
|
||||
@@ -98,7 +100,7 @@
|
||||
|
||||
Phase 4 的核心安全属性(启动无注入面、更新不强杀、自更新可回滚且健康门控、切换临界区复查闭合 Phase 3 O1、Windows 隔离 + fail-closed stub)全部到位,失败码体系完整。建议处理顺序:
|
||||
|
||||
1. **[高优先级 · 可靠性/测试]** O1/O4:补自更新健康超时后的 Recover/锁阻塞恢复、新进程健康门禁、transaction phase 崩溃恢复与 journal/rename/sync/cleanup 失败注入测试;真机锁语义归 T-601。
|
||||
1. **[最高优先级 · 恢复修复与测试]** O1/O4:T-617 先修 prepared rename+sync 后 target/backup 与 journal 不一致,再补自更新健康超时后的 Recover/锁阻塞恢复、新进程健康门禁、transaction phase 崩溃恢复与 journal/rename/sync/cleanup 失败注入测试;真机锁语义归 T-601。
|
||||
2. **[准确性 · 装配规划]** O2/O5:文档保持"无头 core 已完成、端到端未接通"表述;可信 Catalog/下载消费、T-502 授权和自更新发布源分别落地,不笼统等待单一任务。
|
||||
3. **[后续]** O3:T-502 接入真实授权策略并补端到端授权测试。
|
||||
|
||||
@@ -126,6 +128,7 @@ Phase 4 的核心安全属性(启动无注入面、更新不强杀、自更新
|
||||
### 采纳
|
||||
|
||||
- **接受 O4**,并同意其定级:这是**测试覆盖缺口,不是已证实的实现缺陷**(代码在锁阻塞时 fail closed、保留 journal/backup、不强杀)。但缺少故障注入与中断 phase 恢复测试,意味着该状态机的持久化边界行为"构造上正确"而**未被测试证明**,且后续回归无法拦截。对自更新器(失效即"盒子自己更新不动")值得列高优先级。
|
||||
- **T-617 追加发现(2026-07-19):**prepared journal 后首次 rename 已完成但 directory sync 失败时,实际 target/backup 拓扑与 `phasePrepared` 的旧恢复假设不一致;`Recover` 不能再无条件删除该 journal。此项是由既有 `DirectorySyncer` seam 可复现的恢复正确性缺陷,与 O4 的测试覆盖缺口不同;T-617 需作最小状态机修复并以故障测试证明。它不推翻原有“无命令注入、不强杀、无越界移动”的安全裁定。
|
||||
- **接受 O5** 对原 O2 的细化:装配前置不应笼统归为"等 T-502"。可信 Catalog 发布配置、签名清单加载/缓存/目标过滤、下载完成文件受控消费可在 T-502 前独立接入;T-502 只决定授权门禁;自更新另缺可信包下载/验签/版本选择/UI 触发器。按四条分别落任务与验收。
|
||||
- **接受验证段**:`go test -count=10` 对并发/恢复代码是恰当的抗 flakiness 实践;`-race` 因 `CGO_ENABLED` 禁用无法运行并如实声明"不得表述为 race detector 已通过",该诚实度正确。
|
||||
|
||||
@@ -143,4 +146,4 @@ Phase 4 的核心安全属性(启动无注入面、更新不强杀、自更新
|
||||
3. **[后续]** O3:T-502 接入真实授权策略并补端到端授权测试。
|
||||
4. **[真机]** T-601:Windows 文件锁 / 杀毒软件 / 断电故障注入,不由单元测试替代。
|
||||
|
||||
> 裁定:Codex 本轮补充(O4 自查、O5 细化、验证段)全部成立;原审核在测试覆盖维度不如本轮严谨,予以采纳。Phase 4 安全总结论(无注入面、不强杀、可回滚且健康门控、Phase 3 O1 已闭合)双方一致,不需回滚或返工。
|
||||
> 裁定:Codex 本轮补充(O4 自查、O5 细化、验证段)全部成立;原审核在测试覆盖维度不如本轮严谨,予以采纳。随后发现的 prepared rename+sync 恢复不一致由 T-617 进行最小定向修复;Phase 4 安全总结论(无注入面、不强杀、健康门控、Phase 3 O1 已闭合)仍成立,不需要回滚既有 Phase 4 实现或扩大范围返工。
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
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 锁与发布侧装配继续后置。
|
||||
Reference in New Issue
Block a user