Define I/O diagnostic remediation task (T-615)

This commit is contained in:
ila
2026-07-19 20:13:03 +08:00
parent 09f56478d3
commit d1603c52b6
4 changed files with 75 additions and 8 deletions
+2 -2
View File
@@ -47,7 +47,7 @@ SoftBox 软件盒子是一个使用 Go + Gio 开发的 Windows 桌面客户端,
## 当前阶段
当前项目已完成 Phase 0~2、T-301~T-303 与审核整改 `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-401 进程检测与软件启动。物理断电、文件锁与杀毒软件干扰验证保留到 T-601 发布前环境验证。
当前项目已完成 Phase 0~2、T-301~T-303 与审核整改 `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 已正式落成,下一步先关闭 staging 输出 I/O 根因与磁盘满诊断,再正式落成 T-401 进程检测与软件启动。物理断电、文件锁与杀毒软件干扰验证保留到 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-614:图标缓存资源边界、UI 线程事件接线、双 Gio 适配器交互契约、`VisibleItems` generation 生命周期、双端 `shell.go` 同 package 镜像职责拆分、unsafe cache 诊断/人工恢复指引、ZIP 中央目录/EOCD 预扫描、安装耐久顺序和 Catalog 静态签名向量;已完成 T-302:已验签 Catalog 选择与同句柄 size/SHA、严格 app.json、安全 staging/switch/健康与记录写回滚链路。下一步正式落成并执行 T-303,再继续 Phase 4-6。T-601 仍须补真实 Windows 环境的断电/干扰注入。
5. 已完成 T-606~T-614:图标缓存资源边界、UI 线程事件接线、双 Gio 适配器交互契约、`VisibleItems` generation 生命周期、双端 `shell.go` 同 package 镜像职责拆分、unsafe cache 诊断/人工恢复指引、ZIP 中央目录/EOCD 预扫描、安装耐久顺序和 Catalog 静态签名向量;已完成 T-302/T-303:已验签 Catalog 选择与同句柄 size/SHA、严格 app.json、安全 staging/switch/健康与记录写回滚链路,以及 staging 前磁盘/运行状态预检与稳定失败码。下一步执行 T-615,关闭 staging 输出 I/O 根因保留与磁盘满诊断,再继续 T-401 与 Phase 4-6。T-601 仍须补真实 Windows 环境的断电/干扰注入。
## 领取任务规则
+9 -1
View File
@@ -83,11 +83,19 @@ Phase 3 的 T-301 → T-302 → T-303 是安全关键依赖链,任务之间保
| T-302 | 安装流程整合 | T-301, T-102, T-103 | 完整链:下载→SHA-256→app.json 比对→安全解压→staging→切换→健康检查→回滚;`04-architecture.md` 关键安全流程顺序逐步可观察 |
| T-303 | 失败处理与磁盘预检查 | T-302 | 哈希不符、磁盘不足、包损坏、程序占用各有确定结果与错误码;不破坏旧版本 |
#### Phase 3 交叉复核整改
`docs/review/phase3-review.md` 的交叉复核裁定:在继续落成 T-401 前,先关闭 T-303 未覆盖的 staging 输出 I/O 根因保留与磁盘满统一诊断;运行状态切换临界区复查随 T-401 落成。
| ID | 任务 | 依赖 | 验收要点 |
| --- | --- | --- | --- |
| T-615 | 保留安装输出 I/O 根因并统一磁盘满诊断 | T-303 | 区分 ZIP 输入与 staging 输出错误;write/sync/close 的可识别磁盘满统一 `disk_full` 且保留 `errors.Is` 根因;清理失败可观察并安全恢复,不破坏旧版本 |
### Phase 4 · 启动、运行与更新
| ID | 任务 | 依赖 | 验收要点 |
| --- | --- | --- | --- |
| T-401 | 进程检测与软件启动 | T-302 | Toolhelp 快照检测运行状态;启动前检查文件/兼容/占用;WorkingDirectory 正确 |
| T-401 | 进程检测与软件启动 | T-303, T-615 | Toolhelp 快照检测运行状态;启动前检查文件/兼容/占用;切换临界区复查运行状态;WorkingDirectory 正确 |
| T-402 | 子软件更新流程 | T-401 | 可更新识别→确认退出→更新→失败回滚仍可启动旧版;更新不触碰 data/licenses |
| T-403 | SoftBoxUpdater 盒子自更新 | T-402 | 独立 Updater 按 CLI 合约工作;更新中断可恢复;新版健康状态写入 |
+5 -5
View File
@@ -12,8 +12,8 @@
## 当前快照
- 日期:2026-07-18
- 阶段:Phase 2 已完成(T-201~T-204);Phase 3 的 T-301 可恢复下载队列、T-302 安装流程整合与 T-303 失败处理/磁盘预检查已完成;审核整改 T-604~T-614 已完成;下一步应正式落成并执行 T-401
- 日期:2026-07-19
- 阶段:Phase 2 已完成(T-201~T-204);Phase 3 的 T-301 可恢复下载队列、T-302 安装流程整合与 T-303 失败处理/磁盘预检查已完成;审核整改 T-604~T-614 已完成;T-615 已正式落成,下一步领取并执行 staging 输出 I/O 根因保留与磁盘满诊断整改
- 技术栈:根 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 安装 use case(`core/application/install.InstallService` 只取已过滤 Catalog entry + architecture,强制注入 disk/target-state checker;`Extractor.ExtractVerifiedFileWithCheck` 在同一普通文件句柄按 size→SHA-256→EOCD/ZIP64→严格 app.json→已规划 payload 的 staging 前预检→安全 staging 的顺序处理,空间要求为 payload+64 MiB,失败返回稳定 code 且不触发 switch;每个实际 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/T-303 同句柄 package size/SHA、严格/有界 app.json、verified payload 预检 hook、容量精确阈值/故障、程序运行/状态故障、稳定安装失败码、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` 详情语义;安装恢复矩阵保持通过
@@ -21,14 +21,14 @@
- 标准启动路径:`./init.sh` / `./init.ps1`(同步依赖、执行完整 Phase 0 闸门、打印双目标构建命令)
- 标准验证路径:`bash scripts/verify_phase0.sh` / `./scripts/verify_phase0.ps1`
- 版本管理:git 已初始化,main 分支,远端 origin 为 Gitea `opc/soft_quay`;harness 文档已提交
- 当前 blocker:无;T-614 已以静态 corpus 冻结客户端 Catalog canonicalization/签名行为,外部 `softbox-catalog` 消费 corpus 的 CI 证据仍需跨仓库协调,但不阻止 T-303。物理断电、文件锁/杀毒软件干扰仍需 T-601 的目标 Windows VM/真机故障注入
- 当前 blocker:T-615 必须先关闭 staging 输出 I/O 错误链、磁盘满分类和清理失败恢复语义,才可落成 T-401;T-614 已以静态 corpus 冻结客户端 Catalog canonicalization/签名行为,外部 `softbox-catalog` 消费 corpus 的 CI 证据仍需跨仓库协调,但不阻止 T-615。物理断电、文件锁/杀毒软件干扰仍需 T-601 的目标 Windows VM/真机故障注入
## 当前目录要点
| 路径 | 状态 | 说明 |
| --- | --- | --- |
| `docs/` | 已有 | harness coding 文档集(本次初始化完成) |
| `docs/tasks/` | 已有 | Phase 0~2、T-301~T-303 与 T-604~T-614 已完成;下一步按路线图落成 T-401 进程检测与软件启动任务 |
| `docs/tasks/` | 已有 | Phase 0~2、T-301~T-303 与 T-604~T-614 已完成;T-615 已正式落成,待领取执行;之后落成 T-401 进程检测与软件启动任务 |
| `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-303`;审核整改 `T-604`~`T-614`。
- 正在进行:无。
- 下一个可领取任务:暂无;应按 Phase 4 路线图先将 T-401 进程检测与软件启动正式落成任务文件,再领取。T-601 的物理断电与干扰故障注入仍保留为发布前环境验证。
- 下一个可领取任务:T-615(依赖 T-303 已完成);T-401 需等待 T-615 完成后再正式落成。T-601 的物理断电与干扰故障注入仍保留为发布前环境验证。
## 当前可运行内容
+59
View File
@@ -0,0 +1,59 @@
---
id: T-615
title: 保留安装输出 I/O 根因并统一磁盘满诊断
phase: 3
deps: [T-303]
status: TODO
created: 2026-07-19
issue: null
context_ref: null
claim_branch: null
work_branch: null
write_paths:
- docs/tasks/T-615.md
- core/installer/
- core/application/install/
- docs/api.md
- docs/04-architecture.md
- docs/00-ai-start-here.md
- docs/06-tasks.md
- docs/current-state.md
---
## 问题 / 背景
T-303 的磁盘预检只能 fail-fast,不能消除“查询后被其他进程占满”的窗口。当前 `Extractor.extractPlan` 将 `io.Copy` 的所有失败包成 `ErrArchiveCorrupt`,并以 `%v` 丢弃原始 error chain;staging 写入的 ENOSPC 因而可能被错误映射为 `zip_corrupt`。此外,staging 清理使用忽略错误的 `os.RemoveAll`:旧 `current` 因尚未进入 switch 而安全,但未清理的 staging 会让后续安装缺少明确的恢复语义。
这不是包信任边界绕过,但违反 T-303 的稳定失败码与可由 `errors.Is` 识别根因的诊断契约。`docs/review/phase3-review.md` 的交叉复核将其裁定为 P1;必须在 T-401 装配真实运行状态/启动能力之前关闭。
## 方案
1. 在 `core/installer` 区分 ZIP 输入(打开、读取、CRC/关闭)失败与 staging 输出(目录/文件创建、写入、sync、close)失败。前者继续归入 `ErrArchiveCorrupt`;后者使用稳定的 staging-output sentinel,并保留底层 OS 错误的 `errors.Is` 链。不得把目标写入错误伪装为“read ZIP”。
2. 将 extraction 失败后的 staging 清理由“忽略 `RemoveAll` 错误”改为受控、可观察的 managed staging 清理。若主失败与清理失败并存,返回能同时被 `errors.Is` 识别的错误;下一次 `Recover` 必须只在已验证 app layout 内收敛残留 staging,或 fail closed 并给出确定的恢复错误,不能静默覆盖 `current`。
3. 在 `core/application/install` 为平台提供的、可测试的 `StorageFailureClassifier` 注入边界;它只判断已保留的底层 I/O 错误是否为磁盘满,core 不 import Windows API。`InstallServiceConfig` 必须显式要求该依赖;可识别的预检、write、sync、close 磁盘满均映射为 `disk_full`,其他 staging 输出 I/O 仍映射 `install_failed`,ZIP/CRC 输入错误仍映射 `zip_corrupt`。
4. 同步 `docs/api.md`/架构中的失败语义,并以内部可注入的 file-operation/failure seam 补齐测试:输入 CRC 损坏、staging write/sync/close 的 ENOSPC、普通输出 I/O、清理失败和后续恢复。测试必须证明原始根因保留、错误码准确、已有 `current`/`installed-app.json` 不变、首次安装不产生可执行 `current`。
## 验收要点
- ZIP/CRC 输入失败仍有 `ErrArchiveCorrupt` 和 `zip_corrupt`;staging 输出写入失败不携带该 sentinel,且原始输出错误可由 `errors.Is` 识别。
- fake `StorageFailureClassifier` 证明预检、write、sync、close 任一可识别 ENOSPC 都返回 `FailureCodeDiskFull`;无法识别的输出 I/O 返回 `FailureCodeInstallFailed`,不泄漏原始错误给 UI 合约。
- 清理失败不被吞掉;主 extraction 错误和 cleanup 错误均可由 `errors.Is` 检测。后续 `Recover` 只处理已验证的 managed staging,不能影响 `current`、`data/` 或 `licenses/`。
- 更新失败时旧 `current` 与旧 `installed-app.json` 字节保持不变;首次失败不留下 executable `current`。所有新增构造路径都必须显式提供 disk、target-state 与 storage-failure classifier,不能静默降级。
- `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、package、app manifest、installed-app 或下载任务 Schema,不改变 T-302 的同句柄验证、staging/switch/rollback 顺序。
- 不实现 Toolhelp、运行状态复查、等待退出、强杀、启动程序、Gio 安装编排或下载完成文件消费;O1 进入 T-401,O3 留给后续下载→安装生产编排任务。
- 不在 core import Windows API、Gio、SQLite 或第三方依赖;不以当前 OS errno 常量取代平台分类接口。
- 不做物理断电、杀毒软件或文件锁真机故障注入(T-601)。
## 协作约束
- 当前项目为单 Agent 串行模式;当前 Agent 独占全部 `write_paths`,自行完成设计、安全复核、测试和提交,不启动子 Agent。
- 先提交本任务规格及路线图/架构/状态文档;领取后将本文件改为 `DOING`,填写当前 HEAD `context_ref` 与 `work_branch: agent/codex/T-615`,再运行基线和实现。
- T-401 仅在本任务 `DONE`、验证和实现提交后再正式落成;其规格必须消费 T-303 的 `TargetStateChecker`,并实现切换临界区的最后一次运行状态复查。
## 执行记录
- 2026-07-19:正式落成。根据 Phase 3 交叉复核冻结输出 I/O/ZIP 输入的错误分界、可注入磁盘满分类、清理失败恢复语义及 T-401/O1 与后续编排/O3 的边界。