6.3 KiB
6.3 KiB
id, title, phase, deps, status, created, issue, context_ref, claim_branch, work_branch, write_paths
| id | title | phase | deps | status | created | issue | context_ref | claim_branch | work_branch | write_paths | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| T-615 | 保留安装输出 I/O 根因并统一磁盘满诊断 | 3 |
|
DONE | 2026-07-19 | null | d1603c52b6 |
null | agent/codex/T-615 |
|
问题 / 背景
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 装配真实运行状态/启动能力之前关闭。
方案
- 在
core/installer区分 ZIP 输入(打开、读取、CRC/关闭)失败与 staging 输出(目录/文件创建、写入、sync、close)失败。前者继续归入ErrArchiveCorrupt;后者使用稳定的 staging-output sentinel,并保留底层 OS 错误的errors.Is链。不得把目标写入错误伪装为“read ZIP”。 - 将 extraction 失败后的 staging 清理由“忽略
RemoveAll错误”改为受控、可观察的 managed staging 清理。若主失败与清理失败并存,返回能同时被errors.Is识别的错误;下一次Recover必须只在已验证 app layout 内收敛残留 staging,或 fail closed 并给出确定的恢复错误,不能静默覆盖current。 - 在
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。 - 同步
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字节保持不变;首次失败不留下 executablecurrent。所有新增构造路径都必须显式提供 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,填写当前 HEADcontext_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 的边界。
- 2026-07-19:领取任务,基于
d1603c52b6c2df645db11a808ced9a3cc8b3e764在agent/codex/T-615执行;先运行基线验证,再实现与复核。 - 2026-07-19:完成。
Extractor以内部 file-operation seam 区分 ZIP 输入与 staging 创建/write/sync/close 输出,新增可保留原始错误链的ErrStagingOutput;清理失败以ErrStagingCleanup与主失败共同返回。无 journal 的残留 staging 仅在通过 app layout 校验后由 Recover 删除,不影响 current、installed-app、data 或 licenses。InstallService强制注入StorageFailureClassifier,只对ErrStagingOutput中的平台可识别磁盘满返回disk_full,其他输出 I/O 返回install_failed。 - 2026-07-19:验证通过:基线
./init.ps1;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。新增写/sync/close ENOSPC、普通输出 I/O、CRC 分界、清理失败及后续恢复测试。