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

73 lines
8.7 KiB
Markdown
Raw Permalink 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-401
title: 进程检测、受控启动与切换临界区复查
phase: 4
deps: [T-303, T-615]
status: DONE
created: 2026-07-19
issue: null
context_ref: d0cf3333946af1eaace7b9c7f1edc58c38891f4d
claim_branch: null
work_branch: agent/codex/T-401
write_paths:
- docs/tasks/T-401.md
- core/application/install/
- core/application/launch/
- core/installer/
- core/storage/
- app-modern/platform/windows/
- app-win7/platform/windows/
- app-modern/go.mod
- app-win7/go.mod
- app-win7/go.work.sum
- app-modern/cmd/softbox/
- app-win7/cmd/softbox/
- docs/api.md
- docs/04-architecture.md
- docs/00-ai-start-here.md
- docs/06-tasks.md
- docs/current-state.md
- schemas/installed-app.schema.json
---
## 问题 / 背景
T-303 已定义 `TargetStateChecker` 并在 staging 前拒绝运行中的旧版本,但当前只有测试构造者;两个 `platform/windows` 仅返回 edition,未实现 Toolhelp 进程快照,也没有启动器。更重要的是,解压结束到 `current → backup` 之间仍可能启动旧版本。Phase 3 交叉复核要求在 rename 紧邻前显式复查;只有该检查明确返回运行中,才可映射 `app_running`,不得把权限、杀毒或目录错误的 rename 失败误归因。
当前 `installed-app.json` 只有 ID、版本和 payload files,未保留已验证 entrypoint、working directory、min_os 与 requires_admin;因此启动器不能在脱离 Catalog/下载任务时从本地可信记录安全推导启动路径,也不能做系统兼容与 elevation 预检。Gio shell 目前只展示“实际操作将在后续用例接入”,命令层也尚未装配安装或启动工作流;本任务必须如实保持这一现实,不能因此把未完成的下载→安装消费链或授权实现伪装为已完成。
## 方案
1. 在两个 `platform/windows` 增加相同的进程与启动适配边界。Windows 实现以 Toolhelp32 进程快照枚举并以规范化的绝对 entrypoint 身份匹配,不按可碰撞的 exe basename 匹配;通过内部 snapshot seam 覆盖枚举、条目和错误。非 Windows stub 对运行状态与启动返回明确“不支持/不可用”错误,绝不把无法检测伪装为“不在运行”。启动实现只执行已验证的绝对 EXE,显式设置 working directory,不经 shell、URL、PATH 搜索或任意 UI 字符串拼接;需要 elevation 时由 Windows 平台边界按既定 `requires_admin` 语义处理。
2. 扩展 `installed-app.json` v1 的可选启动元数据(`entrypoint`、`working_directory`、`min_os`、`requires_admin`):新安装必须从已比对的 Catalog/app manifest 写入并严格复用安全相对路径与已知 OS/布尔规则;旧 v1 记录保持可读,但没有完整启动元数据时启动 fail closed,不臆测默认 EXE 或工作目录。同步 schema、storage 校验、T-302 写记录路径和文档。
3. 新建纯 core 的 `application/launch` use case,只通过明确接口读取本地记录/受控 current 路径、检查系统兼容、授权和当前运行状态,再调用平台 launcher。它必须验证 `current`/working directory 为真实受控目录、entrypoint 为普通非 symlink 文件且仍位于 current 内;启动请求只含 app ID,不能接受任意 exe/path/args。授权是必需注入边界,T-401 只定义/消费它,不实现许可证策略、更不能使用 allow-all 生产实现。稳定 launch 结果码、错误和 `AppStarted` 事件只携带安全的 app ID/PID/码,原始错误留给后台诊断。
4. `InstallService` 显式消费既有 `TargetStateChecker`,通过 installer 的可注入 pre-switch hook 在存在旧 `current` 时、`current → backup` rename 紧邻前复查。明确返回运行中时清理本次 staging 并返回 `app_running`;检测错误返回 `target_state_unavailable`;其他 rename 错误仍保持原有 switch/rollback 归因。首次安装没有旧 current 时不执行第二次检查。这个 hook 不 import Windows API,也不等待、强杀或启动进程。
5. 两端 cmd 只装配可复用的 platform/core 启动依赖与后台提交边界,Gio Layout 仅提交 app ID,不做磁盘、进程或启动 I/O。若现有命令缺少 Catalog/许可证/下载消费的生产源,必须保留显式未装配状态并在 `current-state.md` 记录,不能伪称端到端闭环;T-402 只在 T-401 完整验证和提交后再落成。
## 验收要点
- Toolhelp 适配在 Windows 以完整 entrypoint 身份判断运行状态;snapshot 创建、迭代或路径查询失败可由调用者识别,并在 core 映射 `target_state_unavailable`。两个 workspace 均有相同 public contract、Windows build 与非 Windows stub 测试。
- 解压期间第二次检查发现旧 current 正在运行时,安装返回 `InstallStageSwitch` + `app_running`,旧 `current` 与 `installed-app.json` 字节不变,本次 staging/journal 被安全清理;第二次检查故障返回 `target_state_unavailable`。首次安装只走 staging 前检查。
- 新安装写入完整启动元数据;缺失/非法元数据、unsafe current/entrypoint、缺文件、系统不兼容、未授权、已运行与启动器失败均有稳定 launch 结果且不启动任何进程。启动器接收的 entrypoint/working directory 必须是 current 内的绝对路径;测试证明不调用 shell/PATH 且工作目录正确。
- 授权、兼容、进程和启动 I/O 均在后台 use case/平台层;Gio Layout 不做 I/O。`AppStarted` 只使用 app ID/PID,UI 不显示 raw error。没有真实许可证实现时,cmd 不得注入 allow-all 或宣称授权闭环完成。
- `go -C core vet ./...`、`go -C core test -count=1 ./...`、`go -C core test -count=10 ./application/install ./application/launch ./installer`、两个 Windows 交叉编译、`./scripts/verify_phase0.ps1`、`python scripts/validate_agent_context.py` 与 `python scripts/validate_harness_governance.py` 全部通过;执行记录写明是否存在尚未装配的真实 Catalog/许可证/下载消费来源。
## 边界(不改什么)
- 不实现下载 completed 文件的安全消费、失败保留或删除协议,不让 `InstallService` 删除任意 `DownloadPath`(O3 留给后续正式下载→安装编排)。
- 不实现等待退出、提示保存、超时、强杀、命名管道 `prepare_update/ready` 或物理 Windows 文件锁结论;前四项属于后续更新协议,锁/杀毒/断电真机验证属于 T-601。
- 不实现许可证签发、机器指纹、试用、导入或 allow-all 授权策略;这些属于 T-501~T-503。不得修改 Catalog、package、下载任务协议或 Gio 版本/Go 工具链。
- 不将 Windows API、Gio、SQLite 或第三方依赖引入 core;不执行 package 内任意脚本,不开放 UI/CLI 传入任意 exe、路径、参数或 shell command。
## 协作约束
- 当前项目为单 Agent 串行模式;当前 Agent 独占全部 `write_paths`,自行完成设计、安全复核、测试和提交,不启动子 Agent。
- 先提交本任务规格及路线图/状态文档;领取后将本文件改为 `DOING`,填写当前 HEAD `context_ref` 与 `work_branch: agent/codex/T-401`,再运行基线和实现。
- T-402 仅在本任务 `DONE`、验证和实现提交后再正式落成;若生产 cmd 仍无可信 Catalog/许可证/下载消费装配,必须在状态文档中明确其非闭环事实而非擅自扩展本任务。
## 执行记录
- 2026-07-19:正式落成。依据 Phase 3 交叉复核冻结 Toolhelp 身份匹配、启动元数据、受控启动、授权注入和 switch 临界区复查;明确 O3、退出协议、许可证策略与 T-601 真机验证的边界。
- 2026-07-19:领取任务,基于 `d0cf3333946af1eaace7b9c7f1edc58c38891f4d` 在 `agent/codex/T-401` 执行;先重新记录基线,再按任务规格实现。
- 2026-07-19:完成。`installed-app.json` 新安装写入受验证启动元数据;`application/launch` 只接受 app ID,按受控 current/普通 entrypoint、兼容、授权、精确运行状态和平台启动器顺序 fail closed。双端 Windows 平台以 Toolhelp32 + 完整映像绝对路径匹配、`RtlGetVersion` 兼容判断和无参数受控启动(管理员包固定 `runas`)实现,非 Windows stub 明确返回不支持。更新在 current→backup 紧邻前复查并清理拒绝的 staging,旧 current/记录不变;首次安装跳过复查。当前 cmd 没有可信 Catalog、许可证或 download 消费源,因此未注入 allow-all 授权、未伪装端到端启动按钮;Gio Layout 仍无 I/O。验证通过:`go -C core vet ./...`、`go -C core test -count=1 ./...`、`go -C core test -count=10 ./application/install ./application/launch ./installer`、两端 Windows amd64 构建、`./scripts/verify_phase0.ps1`、`python scripts/validate_agent_context.py`、`python scripts/validate_harness_governance.py`。