Define failure handling task (T-303)
This commit is contained in:
@@ -47,7 +47,7 @@ SoftBox 软件盒子是一个使用 Go + Gio 开发的 Windows 桌面客户端,
|
||||
|
||||
## 当前阶段
|
||||
|
||||
当前项目已完成 Phase 0~2、T-301、T-302 与审核整改 `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。物理断电、文件锁与杀毒软件干扰验证保留到 T-601 发布前环境验证。
|
||||
当前项目已完成 Phase 0~2、T-301、T-302 与审核整改 `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 已正式落成,下一步按其规格领取并实现失败处理与磁盘预检。物理断电、文件锁与杀毒软件干扰验证保留到 T-601 发布前环境验证。
|
||||
|
||||
优先路径:
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ T-301 下载队列:
|
||||
|
||||
必须防止:绝对路径、`../` 与 Windows dot-space 归一化穿越、首尾空格/尾随句点路径别名、DOS 设备名、符号链接逃逸、写入其他软件目录、覆盖 data 与 licenses、运行中强替换 EXE、未验证包被执行、解压数量/体积/压缩比无上限、包内自动执行脚本。
|
||||
|
||||
Phase 1 ZIP 原型采用“两阶段解压”:第 0 阶段在任何 `zip.Reader` 构造前,从同一普通文件句柄核对 `expectedPackageSize` 与实际长度,并只读有界 EOCD 尾部及固定 ZIP64 end 记录以限制原始包、中央目录和声明条目数;第 1 阶段才由标准库解析完整中央目录,继续预检协议顶层、共享 Windows 安全路径、类型、重复项、entrypoint 与展开资源上限,再规划并确认所有 native 输出路径仍在 destination 内。全部通过后才创建新的 staging 并只写 `payload/`;任一复制/CRC 失败删除本次 staging。T-302 把这一原型封装为同句柄 `size → SHA-256 → scan → app.json → extract` 的生产安装链:app.json 读取有 1 MiB 上限并严格对齐可信 Catalog,实际写入文件 hash 进入 installed-app record;原型默认限制见 [api.md](api.md),真实包分布复核仍是本任务验收的一部分。
|
||||
Phase 1 ZIP 原型采用“两阶段解压”:第 0 阶段在任何 `zip.Reader` 构造前,从同一普通文件句柄核对 `expectedPackageSize` 与实际长度,并只读有界 EOCD 尾部及固定 ZIP64 end 记录以限制原始包、中央目录和声明条目数;第 1 阶段才由标准库解析完整中央目录,继续预检协议顶层、共享 Windows 安全路径、类型、重复项、entrypoint 与展开资源上限,再规划并确认所有 native 输出路径仍在 destination 内。全部通过后才创建新的 staging 并只写 `payload/`;任一复制/CRC 失败删除本次 staging。T-302 把这一原型封装为同句柄 `size → SHA-256 → scan → app.json → extract` 的生产安装链:app.json 读取有 1 MiB 上限并严格对齐可信 Catalog,实际写入文件 hash 进入 installed-app record;原型默认限制见 [api.md](api.md),真实包分布复核仍是本任务验收的一部分。T-303 在严格验证和 extraction 之间加入唯一的 pre-extract 边界:提取器只输出已规划 payload 的 bytes/files 与安全 entrypoint,`core/application/install` 注入容量/目标状态 checker 并在创建 staging 前要求 `payload bytes + 64 MiB` 可用空间及目标未运行;checker 故障 fail closed。core 不包含 Windows API、进程枚举、等待、强杀或启动,具体 Toolhelp 适配与进程退出协议由 T-401 在 `platform/windows`/命令装配时实现。
|
||||
|
||||
Phase 1 原子切换原型把 `install-transaction.json` 与目录现实共同作为恢复依据。阶段写入顺序为 `prepared → current_backed_up → staging_activated → committed`,健康失败写 `rollback_required`;崩溃恢复不自动信任未健康检查的新 current,而是恢复旧 backup 或撤销首次安装。日志结构见 [api.md](api.md)。
|
||||
|
||||
|
||||
+23
-1
@@ -220,7 +220,29 @@ T-302 在 Switcher 的 health 阶段先运行必需的注入 health check,再
|
||||
|
||||
这些栅栏与注入失败测试只证明代码层面的调用顺序和 fail-closed 行为,不证明断电后硬件/驱动缓存、网络文件系统、文件锁或杀毒软件的物理表现。T-302 已完成代码整合;T-601 仍须在目标 Windows VM/真机执行断电与干扰故障注入。
|
||||
|
||||
### 2.6 下载任务元数据 download-task.json(本地)
|
||||
### 2.6 安装预检与失败码
|
||||
|
||||
在同句柄 size/SHA、ZIP 预扫描和严格 `app.json` 身份比对均通过后,提取器会在创建 `staging/` 前提供已规划 payload 的准确展开字节数、普通文件数和安全 entrypoint。安装 use case 必须以 `payload_bytes + 64 MiB` 查询 app root 所在卷的可用空间;可用空间不足时不创建 staging。预检不能替代写入、同步、切换或回滚阶段的 fail-closed I/O 错误处理。
|
||||
|
||||
安装 use case 同时在上述位置检查当前 `current/<entrypoint>` 是否正在运行。容量与运行状态均通过 core 接口注入;检查失败按不可安全继续处理。v1 不强杀、不启动、不等待进程退出。Windows Toolhelp 枚举、正常退出等待与启动协议属于后续 T-401,不能进入 core。
|
||||
|
||||
安装结果面向调用方的错误码为下表的稳定英文枚举;UI 负责本地化,原始错误只保留给 `errors.Is`、日志和诊断,不得进入 UI payload。
|
||||
|
||||
| code | 含义 |
|
||||
| --- | --- |
|
||||
| `hash_mismatch` | 完成文件长度或 SHA-256 与已验签 Catalog selection 不符 |
|
||||
| `zip_path_escape` | ZIP/app manifest 路径违反共享 Windows 安全相对路径规则 |
|
||||
| `zip_corrupt` | ZIP 结构、CRC 或受限读取/提取不完整,不能作为有效包 |
|
||||
| `package_invalid` | package/app manifest 身份或协议不符合可信 selection |
|
||||
| `disk_full` | 可用空间小于 `payload_bytes + 64 MiB` |
|
||||
| `disk_check_failed` | 无法可靠取得可用空间或得到非法容量值 |
|
||||
| `app_running` | 当前目标程序仍在运行,更新不能替换 |
|
||||
| `target_state_unavailable` | 无法可靠取得目标运行状态 |
|
||||
| `install_failed` | 其他未细分的安装、健康、记录、切换或回滚失败 |
|
||||
|
||||
上述任一预检或验证失败都发生在 switch 前;已有版本的 `current` 和 `installed-app.json` 必须保持可用,首次安装不得留下 executable `current`。
|
||||
|
||||
### 2.7 下载任务元数据 download-task.json(本地)
|
||||
|
||||
每个任务以稳定 `request_id` 为主键,元数据位于 `downloads/tasks/<request_id>.json`,字节文件位于 `downloads/files/<request_id>.part|.download`。本地路径只由客户端从 request_id 派生,不接受 URL 或 Content-Disposition 提供的文件名。
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
## 当前快照
|
||||
|
||||
- 日期:2026-07-18
|
||||
- 阶段:Phase 2 已完成(T-201~T-204);Phase 3 的 T-301 可恢复下载队列与 T-302 安装流程整合已完成;审核整改 T-604~T-614 已完成;下一步应正式落成并执行 T-303
|
||||
- 阶段:Phase 2 已完成(T-201~T-204);Phase 3 的 T-301 可恢复下载队列与 T-302 安装流程整合已完成;审核整改 T-604~T-614 已完成;T-303 失败处理与磁盘预检查已正式落成,下一步领取并执行
|
||||
- 技术栈:根 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 安装 use case(`core/application/install.InstallService` 只取已过滤 Catalog entry + architecture,`Extractor.ExtractVerifiedFile` 在同一普通文件句柄按 size→SHA-256→EOCD/ZIP64→严格 app.json→安全 staging 的顺序处理,每个实际 payload 文件 hash 写入 installed-app;health 或记录写失败经 Switcher 回滚),transaction/switch/rollback/recovery 的 journal、rename、清理经统一 fail-closed 耐久栅栏,Windows 使用目录句柄 FlushFileBuffers)、发布稳定只读 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 镜像职责拆文件
|
||||
- 测试:core 覆盖 Catalog 静态 canonicalization/Ed25519 vectors、非法 surrogate/`-0`/Base64 fail-closed、列表快照 generation/零复制、SemVer/12 状态、本地安装记录、Windows dot-space/设备名/Unicode 折叠路径攻击、ZIP destination 包含性与 EOCD/ZIP64 原始包/中央目录/条目数预扫描、T-302 同句柄 package size/SHA、严格/有界 app.json、payload hash 记录、Catalog 选择拒绝、transaction recovery、health/记录写失败回滚、payload/staging tree/journal/rename/rollback/recovery/cleanup 耐久顺序及错误注入、Windows 原生目录 `FlushFileBuffers`、图标并发/取消/读取边界/LRU、真实目录/symlink fail-closed 与 cache→`unsafe_cache` event、relay 背压与关闭、下载并发/暂停/取消/重试/Range/断连/恢复/事件失败与文件身份替换;两个 app 覆盖 Editor/视图/分类/行/恢复/关闭接线、500 项 viewport、AppID 控件与分类控件生命周期、详情上下文、空状态语义、UI drain 前后、图标失败身份生命周期与 `unsafe_cache` 详情语义;安装恢复矩阵保持通过
|
||||
@@ -28,7 +28,7 @@
|
||||
| 路径 | 状态 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `docs/` | 已有 | harness coding 文档集(本次初始化完成) |
|
||||
| `docs/tasks/` | 已有 | Phase 0~2、T-301、T-302 与 T-604~T-614 已完成;下一步按路线图落成 T-303 失败处理与磁盘预检查任务 |
|
||||
| `docs/tasks/` | 已有 | Phase 0~2、T-301、T-302 与 T-604~T-614 已完成;T-303 失败处理与磁盘预检查已正式落成,待领取执行 |
|
||||
| `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 职责文件 |
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
- 已完成:Phase 0 的 `T-001`~`T-004`;Phase 1 的 `T-101`、`T-102`、`T-103`;Phase 2 的 `T-201`~`T-204`;Phase 3 的 `T-301`、`T-302`;审核整改 `T-604`~`T-614`。
|
||||
- 正在进行:无。
|
||||
- 下一个可领取任务:暂无;应按 Phase 3 路线图先将 T-303 失败处理与磁盘预检查正式落成任务文件,再领取。T-601 的物理断电与干扰故障注入仍保留为发布前环境验证。
|
||||
- 下一个可领取任务:T-303(依赖 T-302 已完成);T-601 的物理断电与干扰故障注入仍保留为发布前环境验证。
|
||||
|
||||
## 当前可运行内容
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
id: T-303
|
||||
title: 失败处理与磁盘预检查
|
||||
phase: 3
|
||||
deps: [T-302]
|
||||
status: TODO
|
||||
created: 2026-07-18
|
||||
issue: null
|
||||
context_ref: null
|
||||
claim_branch: null
|
||||
work_branch: null
|
||||
write_paths:
|
||||
- docs/tasks/T-303.md
|
||||
- core/application/install/
|
||||
- core/installer/
|
||||
- docs/api.md
|
||||
- docs/04-architecture.md
|
||||
- docs/00-ai-start-here.md
|
||||
- docs/current-state.md
|
||||
---
|
||||
|
||||
## 问题 / 背景
|
||||
|
||||
T-302 已把已验签 Catalog selection、同句柄 package 校验、严格 `app.json`、安全 staging 和可回滚 switch 串成无头安装 use case,但失败结果仍主要是内部错误链。调用方尚不能稳定地区分哈希不符、包损坏、可用磁盘不足和已有版本正在运行,也没有在创建 staging 前依据实际解压计划阻止空间不足的写入。
|
||||
|
||||
更新场景中,任何上述失败都必须保留旧 `current` 与其 `installed-app.json`;不能以错误码映射为由提前切换、删除旧版本,或把原始底层错误直接作为 UI 合约。运行状态的 Windows Toolhelp 实现和等待用户正常退出属于 T-401,本任务只建立 core 可注入的预检边界。
|
||||
|
||||
## 方案
|
||||
|
||||
1. 在 `core/installer` 为已经通过同句柄 size/SHA、ZIP 预扫描和 `app.json` 身份比对的包暴露只读的 verified-package pre-extract hook。hook 提供已规划 payload 的精确展开字节数、普通文件数和已验证 entrypoint;它在任何 staging 创建或 payload 写入之前调用。保留现有无 hook 提取入口,避免把 application/平台依赖带入 installer。
|
||||
2. 将 `InstallService` 的构造改为显式配置并强制注入 `DiskSpaceChecker` 与 `TargetStateChecker`。pre-extract hook 对 app root 查询可用空间,并要求至少容纳 payload 展开字节数加固定 64 MiB staging/元数据保留;容量查询失败、非法返回值或可用空间不足一律 fail closed。运行检查只接受已验证的 app ID 与 `current/<entrypoint>` 路径;发现运行、或状态无法可靠取得时一律在 staging 前终止。本任务不启动、终止、等待或枚举进程,也不在 core import Windows API。
|
||||
3. 在 `core/application/install` 定义稳定 `FailureCode` 与带 stage/code/root cause 的安装错误。至少冻结 `hash_mismatch`、`zip_path_escape`、`zip_corrupt`、`disk_full`、`disk_check_failed`、`app_running`、`target_state_unavailable`、`package_invalid` 与兜底 `install_failed`;底层根因继续可由 `errors.Is` 识别,UI/事件只消费稳定 code。把已存在的 package/switch/health/record 错误按此表映射,不能暴露文件路径或原始系统错误作为用户合约。
|
||||
4. 为成功、精确空间阈值、空间不足、容量查询失败、运行中、运行状态查询失败、哈希不符与 ZIP/CRC 损坏补齐单元/集成测试。每个失败都断言 staging 未创建或被清理、旧 current/安装记录保持可用、错误码确定且根因仍可识别;验证通过才更新任务状态。
|
||||
|
||||
## 验收要点
|
||||
|
||||
- `installer` 在所有 Catalog/ZIP/manifest 验证成功后、创建 staging 前,向调用方给出精确 payload bytes/files 与安全 entrypoint;hook 失败不产生 staging/payload 写入。
|
||||
- `InstallService` 无法在缺少 disk 或 target-state checker 时构造。它以 `payload_bytes + 64 MiB` 为所需空间;可用空间小于该值返回 `disk_full`,等于该值允许继续,查询故障返回 `disk_check_failed`。
|
||||
- 对已有版本更新时,`hash_mismatch`、`zip_path_escape`、`zip_corrupt`、`disk_full`、`app_running` 和两种 checker 故障均发生在 switch 前,旧 `current` 和旧 `installed-app.json` 字节保持不变;首次安装不留下 executable `current`。
|
||||
- 所有安装失败以稳定英文 `FailureCode` 报告,原始原因仍可被 `errors.Is` 检测;错误码表已写入 `docs/api.md`,不包含绝对路径、URL、token 或系统原始错误。
|
||||
- `go -C core vet ./...`、`go -C core test -count=1 ./...`、`go -C core test -count=10 ./installer ./application/install`、`./scripts/verify_phase0.ps1`、`python scripts/validate_agent_context.py` 与 `python scripts/validate_harness_governance.py` 全部通过;任务执行记录写明结果。
|
||||
|
||||
## 边界(不改什么)
|
||||
|
||||
- 不修改 Catalog、`app.json`、`files.json`、installed-app Schema 或签名/哈希协议;不改变 T-302 同句柄验证和 switch/rollback 的先后顺序。
|
||||
- 不实现 Toolhelp 进程枚举、等待退出、强杀、启动程序、Gio 状态展示、下载重试或命令层依赖装配;T-401 负责 Windows 运行检测与启动协议,后续 UI/编排任务负责事件展示和实际装配。
|
||||
- 不以磁盘预检查替代写入时的错误处理;预检查与 extract/switch 的所有 I/O 错误仍必须 fail closed 并保留 rollback 语义。
|
||||
- 不引入第三方包、Gio、Windows API、SQLite 或 Go 1.21+ API;不做物理断电、文件锁或杀毒软件故障注入(T-601)。
|
||||
|
||||
## 协作约束
|
||||
|
||||
- 当前项目为单 Agent 串行模式;当前 Agent 独占全部 `write_paths`,自行完成设计、安全复核、测试和提交,不启动子 Agent。
|
||||
- 先提交本任务规格与关联协议/架构/状态文档;领取后将本文件改为 `DOING`,填写当前 HEAD `context_ref` 与 `work_branch: agent/codex/T-303`,再运行基线和实现。
|
||||
- T-401 及任何后置任务在本任务 `DONE`、验证与实现提交前不得领取或提前修改。
|
||||
|
||||
## 执行记录
|
||||
|
||||
- 2026-07-18:正式落成。冻结 verified-package hook、64 MiB 解压保留、强制注入 disk/target-state checker、稳定失败码及 T-401 的进程检测边界。
|
||||
Reference in New Issue
Block a user