From 2d302b731ab907de36814533d15ac7f81f399e35 Mon Sep 17 00:00:00 2001 From: ila Date: Thu, 16 Jul 2026 18:48:15 +0800 Subject: [PATCH] Define multi-agent download workflow (T-301) --- AGENTS.md | 8 +++++ docs/06-tasks.md | 2 ++ docs/current-state.md | 10 +++--- docs/tasks/README.md | 16 +++++++++ docs/tasks/T-301.md | 77 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 docs/tasks/T-301.md diff --git a/AGENTS.md b/AGENTS.md index e46be18..dc027eb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -45,6 +45,14 @@ - 密钥、许可证私钥、真实注册码、真实下载 URL 一律不入库;示例只用占位符。 - 提交信息使用英文祈使句,任务相关提交带上 `T-<编号>`。 +## 多 Agent 协作 + +- 任务依赖仍是硬门槛:T-301 → T-302 → T-303 这类依赖链不得把后置任务提前并发实现。 +- 同一个任务默认只有**一个写入 Agent**拥有任务文件 `write_paths`;其他 Agent 可并行进行只读测试设计、安全审查或代码审查,但不得修改文件、暂存或提交。 +- 主 Agent / coordinator 负责领取、分配角色、汇总只读意见、执行最终验证与创建该任务唯一提交;审查 Agent 只通过消息返回发现和建议。 +- 只有先把大任务拆成独立 `T-<编号><后缀>.md`、冻结共享接口并提交,且各子任务 `write_paths` 无目录前缀重叠时,才允许多个写入 Agent 并行编码。 +- 多 Agent 角色、写入所有权、只读边界和汇总结果必须记录到当前任务的 `## 协作约束` / `## 执行记录`,不能只存在于对话。 + ## 验证 ```bash diff --git a/docs/06-tasks.md b/docs/06-tasks.md index 7f29386..7220f05 100644 --- a/docs/06-tasks.md +++ b/docs/06-tasks.md @@ -43,6 +43,8 @@ ### Phase 3 · 下载与安装 +Phase 3 的 T-301 → T-302 → T-303 是安全关键依赖链,任务之间保持串行。每个任务内部默认采用“一个写入 Agent + 测试设计/安全审查只读 Agents”;只有先落成子任务、提交共享接口且写路径无重叠时才并行编码,完整规则见 [`tasks/README.md`](tasks/README.md) 与仓库 `AGENTS.md`。 + | ID | 任务 | 依赖 | 验收要点 | | --- | --- | --- | --- | | T-301 | 下载队列 | T-202 | 并发默认 2;暂停/取消/重试;Range 续传;任务元数据持久化,重启恢复;事件驱动进度 | diff --git a/docs/current-state.md b/docs/current-state.md index a7be0b8..fc5ff26 100644 --- a/docs/current-state.md +++ b/docs/current-state.md @@ -13,7 +13,7 @@ ## 当前快照 - 日期:2026-07-16 -- 阶段:Phase 2 已完成(T-201~T-204);下一步 Phase 3 的 T-301 下载队列 +- 阶段:Phase 2 已完成(T-201~T-204);T-301 下载队列已落成并领取,准备按单写入者 + 多只读审查者模式执行 - 技术栈:根 Go workspace 纳入 core/app-modern/app-win7 三模块;`app-win7/go.work` 隔离 Go 1.20.14 构建;modern Gio v0.10.1 与 win7 Gio v0.6.0 已实际接入 - 生产代码:core 已有 Catalog/本地状态/存储、无 IO 软件列表模型与按 digest+DPI 的可信图标缓存;modern/win7 AppShell 已实现搜索/分类/视图、惰性列表、详情右栏与内存图标 - 测试:core 覆盖 Catalog、SemVer/12 状态、本地安装记录、列表筛选和图标内存/磁盘/离线/损坏恢复;两个 app 覆盖 500 项虚拟列表、ID 控件稳定性、详情/ApplyIcon 与平台 stub;ZIP/安装恢复矩阵保持通过 @@ -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-301 +- 当前 blocker:无;当前任务 T-301,后置 T-302/T-303 仍受依赖门槛约束 ## 当前目录要点 | 路径 | 状态 | 说明 | | --- | --- | --- | | `docs/` | 已有 | harness coding 文档集(本次初始化完成) | -| `docs/tasks/` | 已有 | Phase 0、Phase 1 与 Phase 2 任务均已完成;T-301 待按路线图落成 | +| `docs/tasks/` | 已有 | Phase 0~2 已完成;T-301 已落成并处于 DOING | | `scripts/` | 已有 | harness 治理、core 边界、Go 版本检查与 Phase 0 双平台验证入口 | | `core/` | 已建 | Go 1.20 兼容;已有正式 Catalog、本地状态/存储、列表模型、图标缓存与 Phase 1 安装安全原型 | | `app-modern/` | 已建 | Go 1.25.0 + Gio v0.10.1;Modern AppShell 已接入虚拟列表、详情和内存图标 | @@ -41,8 +41,8 @@ 任务状态以 `docs/tasks/` 各任务文件 frontmatter 的 `status` 为准。本节只写项目级摘要: - 已完成:Phase 0 的 `T-001`~`T-004`;Phase 1 的 `T-101`、`T-102`、`T-103`;Phase 2 的 `T-201`~`T-204`。 -- 正在进行:无。 -- 下一个可领取任务:按路线图落成并领取 `T-301 下载队列`。 +- 正在进行:`T-301 下载队列`(一个写入 Agent + 测试/安全只读 Agents)。 +- 下一个可领取任务:无;T-302 必须等待 T-301 DONE。 ## 当前可运行内容 diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 309fde3..55f338b 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -53,6 +53,22 @@ write_paths: # 允许修改的仓库相对路径 - **执行记录写进本任务文件的 `## 执行记录` 一节**(改了什么、跑了什么验证、结果、决策)——不逐任务追加共享的 `progress.md`(可选历史归档)、也不逐任务覆盖 `current-state.md`(项目级快照,只在启动/验证路径、目录结构或 blocker 变化时更新)。 - **只改自己那个任务文件**;不要编辑别人正在做的任务文件。 +### 同一任务的多 Agent 模式 + +当任务本身不能安全拆成互不依赖的写路径时,使用“单写入者 + 多只读审查者”: + +1. coordinator 先落成任务文件并提交,明确唯一写入 Agent、只读测试 Agent、只读安全/架构 Agent。 +2. 写入 Agent 独占任务 `write_paths`;只读 Agent 不调用文件编辑、`git add`、`git commit` 或会改变仓库状态的命令。 +3. 只读 Agent 返回可复现的测试矩阵、风险和审查意见;coordinator 决定是否采纳并由唯一写入者或 coordinator 修改。 +4. 所有意见合并后由 coordinator 跑任务完整闸门、更新执行记录并创建唯一任务提交。 + +若要让多个 Agent 并行写代码,必须先: + +- 拆成带后缀的独立任务文件(如 `T-301a.md` / `T-301b.md`)。 +- 先完成并提交共享接口/协议冻结任务。 +- 确保依赖均 DONE,且 `write_paths` 互不重叠、没有目录前缀包含关系。 +- 每个子任务独立状态、验证证据和 Git 提交;最后另设串行集成任务。 + 未启用 Gitea 时,在独立分支 / worktree 中把任务文件从 `TODO` 改为 `DOING` 即可。启用 Gitea 时,必须先按 [`../gitea-collaboration.md`](../gitea-collaboration.md) 由 dispatcher 串行分配并创建 `claims/T-<编号>` 防御性标记;assignee、标签、读回和普通 create-branch API 都不能单独提供并发互斥。 ## 与 Gitea Issue / PR 的映射(可选) diff --git a/docs/tasks/T-301.md b/docs/tasks/T-301.md new file mode 100644 index 0000000..bf9c987 --- /dev/null +++ b/docs/tasks/T-301.md @@ -0,0 +1,77 @@ +--- +id: T-301 +title: 可恢复下载队列 +phase: 3 +deps: [T-202] +status: DOING +created: 2026-07-16 +issue: null +context_ref: a52acbf926041550ebc30049ff87f08faaa5ba0c +claim_branch: null +work_branch: agent/codex/T-301 +write_paths: + - docs/tasks/T-301.md + - core/downloader/ + - core/application/ + - core/storage/ + - schemas/download-task.schema.json + - testdata/download/ + - testdata/README.md + - docs/api.md + - docs/04-architecture.md + - docs/current-state.md +--- + +## 问题 / 背景 + +Phase 2 已能识别软件状态并展示列表/详情,但点击下载后还没有并发调度、暂停/取消/重试、HTTP Range 续传或重启恢复。若下载状态只保存在 goroutine 内存中,盒子退出后任务丢失;若 Range 响应、临时文件和元数据不一致,可能把拼接错误的字节交给 T-302 校验/安装链路。 + +T-301 是 T-302/T-303 的前置安全边界。它只负责把远端 ZIP 稳定下载为隔离的 `.part`/完成文件并发布进度事件,不声称包已可信可执行。 + +## 方案 + +1. 在 `core/downloader` 定义任务模型、队列命令与状态机;默认最多 2 个 active transfer,其余保持 queued。 +2. 支持 enqueue、pause、resume、cancel、retry;每个命令按稳定 `request_id` / `app_id` 定位,重复命令返回确定结果而非产生第二份任务。 +3. HTTP transport 使用注入接口和标准库实现: + - 已有 `.part` 时发送 `Range: bytes=-`。 + - `206` 必须校验 Content-Range 起点;不匹配则拒绝。 + - 服务端返回 `200` 时安全截断并从 0 重下,不得追加到旧字节。 + - 网络中断保留已落盘 `.part`;取消按策略删除任务临时文件;完成前 flush/sync/close。 +4. 在 `core/storage` 持久化下载任务元数据,采用同目录临时文件 + backup 原子替换;Schema 落到 `schemas/download-task.schema.json`。 +5. 启动恢复时: + - queued / paused / failed 保持可恢复语义。 + - 进程退出时遗留的 downloading 降级为 queued,不假装仍在下载。 + - 元数据与 `.part` 实际长度不一致时以磁盘为准或返回稳定损坏错误,不得静默跳过字节。 +6. application 事件沿用 `DownloadStarted` / `DownloadProgress` / `DownloadPaused` / `DownloadCompleted` / `DownloadFailed`;负载使用具体类型,UI 只消费事件,不轮询文件。 +7. 进度包含 done/total/speed;total 未知时明确表示 unknown,不伪造百分比。事件节流与 clock 注入保证测试确定性。 + +## 验收要点 + +- 并发默认值为 2;表驱动/阻塞 transport 测试证明任意时刻 active transfer 不超过 2,完成/暂停后下一个 queued 自动开始。 +- pause 保留 `.part` 和元数据;resume 使用正确 Range;cancel 不再发布进度且按协议清理;failed 可 retry。 +- httptest 覆盖:正常 200、正确 206、错误 Content-Range、服务器忽略 Range 返回 200、连接中断后续传、非成功状态。 +- 任务元数据严格读写;未知字段、非法状态/ID/URL/路径/计数被拒绝;主文件缺失时可读取中断遗留 backup。 +- 新队列实例能从磁盘恢复;遗留 downloading → queued,paused 保持 paused,已完成任务不重复下载。 +- `.part` 长度、done 与下一次 Range offset 一致;不把未完成文件暴露为完成文件。 +- application 具体事件负载与 request_id/app_id 匹配;进度 done 单调不回退,暂停/取消后不出现迟到 progress。 +- `cd core && go vet ./... && go test -count=1 ./...`、完整双目标闸门与治理校验通过。 + +## 边界(不改什么) + +- 不做 ZIP SHA-256 / package 签名、app.json 比对、解压、staging 切换或健康检查(T-302)。 +- 不做磁盘空间预检查、程序占用、hash_mismatch/zip_corrupt 等完整失败矩阵(T-303)。 +- 不实现 Gio DownloadPanel;本任务只交付 core 队列、持久化与事件,后续 UI 通过事件接入。 +- 不实现多源下载、P2P、带宽限速、代理设置或动态并发 UI。 +- 不引入第三方下载器、数据库或 Go 1.21+ API。 + +## 协作约束 + +- 唯一写入 Agent 独占本任务全部 `write_paths`,负责实现但不创建额外任务提交。 +- 测试设计 Agent 只读输出并发、Range、崩溃恢复和事件时序测试矩阵。 +- 安全/架构 Agent 只读审查路径隔离、响应拼接、持久化原子性、迟到事件和 T-302 信任边界。 +- coordinator 汇总意见、要求必要修正、执行最终验证、更新本执行记录并创建唯一 T-301 Git 提交。 +- T-302/T-303 在本任务 DONE 前不得领取;若后续决定并行编码,先另行落成并提交带后缀子任务及共享接口冻结任务。 + +## 执行记录 + +- 2026-07-16:正式落成多 Agent 协作方式;采用一个写入 Agent + 测试设计/安全架构两个只读 Agent,保持 T-301 → T-302 → T-303 依赖串行。