feat(coordination): add atomic Gitea task workflow (phase 2)
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
# Gitea 多 Agent 协作协议
|
||||
|
||||
> 本协议是可选增强。启用 Gitea 协作时,任务规格留在 Git,实时协调放在 Issue / PR;未启用时继续使用 [`tasks/README.md`](tasks/README.md) 的本地流程。
|
||||
|
||||
## 工件与权威来源
|
||||
|
||||
| 工件 | 保存什么 | 不保存什么 |
|
||||
| --- | --- | --- |
|
||||
| `docs/tasks/T-<编号>.md` | 任务规格、依赖、允许写路径、验收标准、可审计执行证据 | Token、实例地址、临时聊天 |
|
||||
| Gitea Issue | 实时状态、领取者、阻塞、结构化 claim / 续租记录 | 需求正文的唯一副本 |
|
||||
| `claims/T-<编号>` 分支 | 唯一领取锁;分支存在表示任务已被领取 | 工作提交 |
|
||||
| `agent/<agent-id>/T-<编号>` 分支 | 单个 agent 的任务提交 | 其他任务的顺手修改 |
|
||||
| Pull Request | 评审、验证证据、合并决策 | 未进入 Git 的隐含上下文 |
|
||||
|
||||
同一个任务只能映射一个任务文件和一个主 Issue。Issue 标题、工作分支和 PR 标题都以 `[T-<编号>]` 开头;Issue 正文保存任务文件路径,PR 同时链接任务文件和 Issue。
|
||||
|
||||
## 状态与标签
|
||||
|
||||
推荐标签:
|
||||
|
||||
- 类型:`kind/task` 标识可执行任务,并从 `type/docs`、`type/code` 中选择一个主要变更类型。
|
||||
- 状态:`status/todo`、`status/doing`、`status/blocked`、`status/review`、`status/done`。
|
||||
- 优先级:`priority/p0`、`priority/p1`、`priority/p2`。
|
||||
|
||||
`status/*`、`type/*`、`priority/*` 分别使用 Gitea exclusive scoped labels,同一分组任一时刻最多一个;`kind/task` 为普通标签。
|
||||
|
||||
状态映射:
|
||||
|
||||
| 阶段 | 任务文件 | Issue | 分支 / PR |
|
||||
| --- | --- | --- | --- |
|
||||
| 待领取 | `TODO` | open + `status/todo` | 无 claim 分支 |
|
||||
| 开发中 | 工作分支上 `DOING` | open + `status/doing` | claim 与工作分支存在 |
|
||||
| 阻塞 | `BLOCKED` | open + `status/blocked` | 默认保留 claim,避免误领 |
|
||||
| 评审中 | 已写完整证据 | open + `status/review` | PR open |
|
||||
| 已完成 | 合入默认分支的 `DONE` | closed + `status/done` | PR merged;claim 可清理 |
|
||||
|
||||
Issue 是实时状态权威;默认分支尚未合入工作提交时,其任务文件仍可能显示 `TODO`,这不是冲突。合并后,任务文件成为长期审计事实。
|
||||
|
||||
## 任务进入可领取队列
|
||||
|
||||
1. 先创建任务文件,写清规格、依赖和初始 `write_paths`,合入默认分支;此时 `issue`、`context_ref`、claim / 工作分支均为 `null`。
|
||||
2. 用 Issue 模板创建唯一主 Issue。新 Issue 只有 `kind/task`,尚未带 `status/todo`。
|
||||
3. 把 Issue 编号回填任务文件,并让 Issue 链接该文件;映射提交合入默认分支后,再选择 `type/*`、`priority/*` 和 `status/todo`。
|
||||
|
||||
因此 dispatcher 能从默认分支可靠定位任务 ↔ Issue;没有双向映射或没有 `status/todo` 的任务都不可领取。
|
||||
|
||||
## 串行分配与原子领取
|
||||
|
||||
仅修改 assignee / `status/doing` 再读回不是原子操作:两个 agent 可能先后覆盖并各自读到成功。MVP 指定一个 dispatcher(主 agent 或维护者)串行执行分配;worker 不并发自选任务。dispatcher 再使用 Gitea “同名分支只能创建一次”的约束,防止同一任务因重试或旁路操作被重复领取:
|
||||
|
||||
1. 读取默认分支任务文件和对应 Issue,确认双向映射、依赖均为 `DONE`、Issue 为 `status/todo`,且目标 worker 没有其他活跃任务。
|
||||
2. 读取默认分支头提交 SHA,记为 `context_ref`。串行检查所有活跃预留的 `write_paths`,不得与本任务重叠。
|
||||
3. 从精确的 `context_ref` 原子创建 `claims/T-<编号>`。创建成功者获得该任务锁;收到 `409` 或“分支已存在”即停止,不得仅因分支 SHA 相同就判定成功。
|
||||
4. 创建 `agent/<agent-id>/T-<编号>` 工作分支,更新 Issue 为 `status/doing`,按项目规则设置 assignee,并追加结构化 claim 评论。
|
||||
5. dispatcher 读回 Issue 和两个分支;不一致时先修复协调状态,不把任务交给 worker。
|
||||
6. worker 在独立 worktree 读回分配结果,再把工作分支任务文件更新为 `DOING`,写入 `context_ref`、claim / 工作分支和已接受的 `write_paths`;提交只触碰允许路径。
|
||||
|
||||
不同任务的“扫描路径后分别创建各自 claim”本身不具备原子性,因此不得让多个 worker 并发执行步骤 1~5。若团队不使用单一 dispatcher,路径检查只能视为乐观预检,任务必须事先由维护者分配互不重叠的范围,不能宣称有强互斥。
|
||||
|
||||
结构化 claim 评论至少包含:
|
||||
|
||||
```text
|
||||
CLAIM
|
||||
task: T-123
|
||||
claimed_by: 【agent-id】
|
||||
allocated_by: 【dispatcher-id】
|
||||
context_ref: 【40 位提交 SHA】
|
||||
claim_branch: claims/T-123
|
||||
work_branch: agent/【agent-id】/T-123
|
||||
write_paths: 【仓库相对路径列表】
|
||||
claimed_at: 【RFC 3339 时间】
|
||||
lease_until: 【RFC 3339 时间】
|
||||
```
|
||||
|
||||
断线重连时,只有 Issue 最新有效 claim 的 `claimed_by`、工作分支和当前 agent 全部一致,才可把已有 claim 当作自己的恢复现场;仅比较 SHA 不足以证明所有权。
|
||||
|
||||
## 写路径防撞
|
||||
|
||||
- 默认分支任务文件定义初始 `write_paths`;领取后,最新被 dispatcher 接受的结构化 claim / scope 评论与工作分支任务文件共同定义活跃预留。二者不一致时暂停工作。
|
||||
- `write_paths` 必须列出任务文件本身及预期修改的文件或目录;共享配置、锁文件、导航文件也要列入。
|
||||
- 两条路径相同,或一条是另一条的目录前缀,视为重叠;活跃任务不得存在重叠路径。
|
||||
- 发现必须修改范围外文件时,先停止并在 Issue 提议扩展范围;dispatcher 串行复查其他活跃预留,接受后追加结构化 scope 评论,worker 同步更新工作分支任务文件,二者都完成后才能继续。
|
||||
- 活跃任务由 Issue 的 `status/doing`、`status/blocked`、`status/review` 判定。每个 agent 同时最多一个活跃任务;项目可以并行多个写路径互不重叠的任务。
|
||||
|
||||
建议 worktree 命令:
|
||||
|
||||
```powershell
|
||||
git fetch origin
|
||||
git worktree add ../【项目】-T-123 -b agent/【agent-id】/T-123 origin/agent/【agent-id】/T-123
|
||||
```
|
||||
|
||||
不要让多个 agent 共用同一 worktree,也不要在 claim 分支提交工作代码。
|
||||
|
||||
## PR 与完成
|
||||
|
||||
1. 在任务文件 `## 执行记录` 写入实际验证命令和结果,完成标准满足后更新状态。
|
||||
2. PR 使用 `.gitea/PULL_REQUEST_TEMPLATE.md`,链接 `Closes #【Issue 编号】`、任务文件、`context_ref`、写路径和验证证据。
|
||||
3. 创建 PR 后把 Issue 切到唯一 `status/review`;评审失败则把 Issue 和工作分支任务状态一起回到 doing / blocked。
|
||||
4. PR 合并、默认分支任务文件为 `DONE` 后,Issue 才切到 `status/done` 并关闭。
|
||||
5. MCP 当前没有删除分支工具。清理 claim 前先确认 PR 已合并、Issue 已完成且无恢复需要,再由维护者通过 Gitea UI 或受控 REST 操作删除。
|
||||
|
||||
## 过期 claim 与断连
|
||||
|
||||
- agent 应在 `lease_until` 前用新评论续租;续租不更换 claim 分支。
|
||||
- claim 过期不等于可以自动抢占。维护者先检查 Issue 最后活动、工作分支新提交和 PR,再评论回收原因并人工删除 claim 分支。
|
||||
- Gitea / MCP 断连时,只能继续已经确认归属自己的任务;不能领取新任务、释放锁或猜测远端状态。
|
||||
|
||||
## 初始化标签
|
||||
|
||||
先预览,再显式写入:
|
||||
|
||||
```powershell
|
||||
python scripts/setup_gitea_labels.py --repo 【owner/repo】
|
||||
python scripts/setup_gitea_labels.py --repo 【owner/repo】 --apply
|
||||
```
|
||||
|
||||
脚本从环境变量读取 `GITEA_URL`、`GITEA_TOKEN`;HTTP 仍要求 `GITEA_ALLOW_INSECURE_HTTP=1`。默认命令会连接目标仓库做只读比较,显示 create / update / unchanged;`--apply` 会把同名标签的颜色、描述和 exclusive 属性校正为本模板值。MCP 没有创建标签工具,因此标签初始化使用 Gitea REST API 或由维护者在 UI 中完成。
|
||||
|
||||
## 并发验收
|
||||
|
||||
在专用测试任务上让两个进程同时创建同一个临时 claim 分支,验收结果必须是恰好一个 `201`、另一个 `409`。测试前后均需人工确认分支名,清理由受控 REST / UI 完成;不得在业务任务上试验。
|
||||
Reference in New Issue
Block a user