--- 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`。