feat(governance): automate harness consistency checks (phase 3)
Harness governance / validate (push) Has been cancelled
Harness governance / validate (push) Has been cancelled
This commit is contained in:
+29
-10
@@ -8,7 +8,7 @@
|
||||
| --- | --- | --- |
|
||||
| `docs/tasks/T-<编号>.md` | 任务规格、依赖、允许写路径、验收标准、可审计执行证据 | Token、实例地址、临时聊天 |
|
||||
| Gitea Issue | 实时状态、领取者、阻塞、结构化 claim / 续租记录 | 需求正文的唯一副本 |
|
||||
| `claims/T-<编号>` 分支 | 唯一领取锁;分支存在表示任务已被领取 | 工作提交 |
|
||||
| `claims/T-<编号>` 分支 | 防御性领取标记;分支存在表示 dispatcher 已分配任务 | 工作提交、跨 dispatcher 的线性化锁 |
|
||||
| `agent/<agent-id>/T-<编号>` 分支 | 单个 agent 的任务提交 | 其他任务的顺手修改 |
|
||||
| Pull Request | 评审、验证证据、合并决策 | 未进入 Git 的隐含上下文 |
|
||||
|
||||
@@ -44,13 +44,13 @@ Issue 是实时状态权威;默认分支尚未合入工作提交时,其任
|
||||
|
||||
因此 dispatcher 能从默认分支可靠定位任务 ↔ Issue;没有双向映射或没有 `status/todo` 的任务都不可领取。
|
||||
|
||||
## 串行分配与原子领取
|
||||
## 串行分配与防重复领取
|
||||
|
||||
仅修改 assignee / `status/doing` 再读回不是原子操作:两个 agent 可能先后覆盖并各自读到成功。MVP 指定一个 dispatcher(主 agent 或维护者)串行执行分配;worker 不并发自选任务。dispatcher 再使用 Gitea “同名分支只能创建一次”的约束,防止同一任务因重试或旁路操作被重复领取:
|
||||
仅修改 assignee / `status/doing` 再读回不是原子操作:两个 agent 可能先后覆盖并各自读到成功。MVP 的互斥保证来自单一 dispatcher(主 agent 或维护者)串行执行分配;worker 不并发自选任务。claim 分支用于识别已分配任务并拦截顺序重试 / 常见旁路,不把 Gitea 的普通 create-branch API 当作线性化锁:
|
||||
|
||||
1. 读取默认分支任务文件和对应 Issue,确认双向映射、依赖均为 `DONE`、Issue 为 `status/todo`,且目标 worker 没有其他活跃任务。
|
||||
2. 读取默认分支头提交 SHA,记为 `context_ref`。串行检查所有活跃预留的 `write_paths`,不得与本任务重叠。
|
||||
3. 从精确的 `context_ref` 原子创建 `claims/T-<编号>`。创建成功者获得该任务锁;收到 `409` 或“分支已存在”即停止,不得仅因分支 SHA 相同就判定成功。
|
||||
3. 从精确的 `context_ref` 创建 `claims/T-<编号>` 防御性标记。若已存在、返回非成功或状态不确定就停止并人工核查;并发冲突在不同版本中可能表现为 `409` 或 `5xx`,不得自动无限重试,也不得仅因分支 SHA 相同就判定本次分配成功。
|
||||
4. 创建 `agent/<agent-id>/T-<编号>` 工作分支,更新 Issue 为 `status/doing`,按项目规则设置 assignee,并追加结构化 claim 评论。
|
||||
5. dispatcher 读回 Issue 和两个分支;不一致时先修复协调状态,不把任务交给 worker。
|
||||
6. worker 在独立 worktree 读回分配结果,再把工作分支任务文件更新为 `DOING`,写入 `context_ref`、claim / 工作分支和已接受的 `write_paths`;提交只触碰允许路径。
|
||||
@@ -63,11 +63,13 @@ Issue 是实时状态权威;默认分支尚未合入工作提交时,其任
|
||||
CLAIM
|
||||
task: T-123
|
||||
claimed_by: 【agent-id】
|
||||
allocated_by: 【dispatcher-id】
|
||||
allocated_by: 【dispatcher 的 Gitea 登录名】
|
||||
context_ref: 【40 位提交 SHA】
|
||||
claim_branch: claims/T-123
|
||||
work_branch: agent/【agent-id】/T-123
|
||||
write_paths: 【仓库相对路径列表】
|
||||
write_paths:
|
||||
- docs/tasks/T-123.md
|
||||
- 【其他仓库相对路径】
|
||||
claimed_at: 【RFC 3339 时间】
|
||||
lease_until: 【RFC 3339 时间】
|
||||
```
|
||||
@@ -76,10 +78,10 @@ lease_until: 【RFC 3339 时间】
|
||||
|
||||
## 写路径防撞
|
||||
|
||||
- 默认分支任务文件定义初始 `write_paths`;领取后,最新被 dispatcher 接受的结构化 claim / scope 评论与工作分支任务文件共同定义活跃预留。二者不一致时暂停工作。
|
||||
- 默认分支任务文件定义初始 `write_paths`;领取后,由配置的 dispatcher Gitea 身份发布、且 `allocated_by` 与评论作者一致的最新完整 CLAIM / CLAIM RENEWAL,与工作分支任务文件共同定义活跃预留。二者不一致时暂停工作。
|
||||
- `write_paths` 必须列出任务文件本身及预期修改的文件或目录;共享配置、锁文件、导航文件也要列入。
|
||||
- 两条路径相同,或一条是另一条的目录前缀,视为重叠;活跃任务不得存在重叠路径。
|
||||
- 发现必须修改范围外文件时,先停止并在 Issue 提议扩展范围;dispatcher 串行复查其他活跃预留,接受后追加结构化 scope 评论,worker 同步更新工作分支任务文件,二者都完成后才能继续。
|
||||
- 发现必须修改范围外文件时,先停止并在 Issue 提议扩展范围;dispatcher 串行复查其他活跃预留,接受后追加包含全部字段和新路径的 `CLAIM RENEWAL`,worker 同步更新工作分支任务文件,二者都完成后才能继续。审计始终以最后一个完整 CLAIM 块为准。
|
||||
- 活跃任务由 Issue 的 `status/doing`、`status/blocked`、`status/review` 判定。每个 agent 同时最多一个活跃任务;项目可以并行多个写路径互不重叠的任务。
|
||||
|
||||
建议 worktree 命令:
|
||||
@@ -101,10 +103,18 @@ git worktree add ../【项目】-T-123 -b agent/【agent-id】/T-123 origin/agen
|
||||
|
||||
## 过期 claim 与断连
|
||||
|
||||
- agent 应在 `lease_until` 前用新评论续租;续租不更换 claim 分支。
|
||||
- worker 应在 `lease_until` 前请求续租;dispatcher 串行复查后,由自己的 Gitea 身份发布包含全部字段的 `CLAIM RENEWAL`。单次租期最长 24 小时,续租不得更换 `task`、`claimed_by`、`allocated_by`、`context_ref`、claim / 工作分支;审计以最后一个由配置 dispatcher 发布的有效块为准。
|
||||
- claim 过期不等于可以自动抢占。维护者先检查 Issue 最后活动、工作分支新提交和 PR,再评论回收原因并人工删除 claim 分支。
|
||||
- Gitea / MCP 断连时,只能继续已经确认归属自己的任务;不能领取新任务、释放锁或猜测远端状态。
|
||||
|
||||
只读检测命令:
|
||||
|
||||
```powershell
|
||||
python scripts/audit_gitea_coordination.py --repo 【owner/repo】 --dispatcher 【Gitea登录名】
|
||||
```
|
||||
|
||||
从默认分支的 clean checkout 运行审计。`--dispatcher`(或非敏感环境变量 `GITEA_DISPATCHER_LOGIN`)指定唯一可信的 dispatcher Gitea 登录名;审计只接受该账号发布且 `allocated_by` 一致的 CLAIM。它会核对标签、任务依赖、任务 ↔ Issue、claim / 工作分支、PR、活跃写路径和 `lease_until`,不会写远端。发现过期 claim 后不自动删除:维护者先查 Issue 最后活动、分支新提交和 PR,再评论回收原因,确认无人继续工作后才通过 UI 或受控 REST 删除。
|
||||
|
||||
## 初始化标签
|
||||
|
||||
先预览,再显式写入:
|
||||
@@ -118,4 +128,13 @@ python scripts/setup_gitea_labels.py --repo 【owner/repo】 --apply
|
||||
|
||||
## 并发验收
|
||||
|
||||
在专用测试任务上让两个进程同时创建同一个临时 claim 分支,验收结果必须是恰好一个 `201`、另一个 `409`。测试前后均需人工确认分支名,清理由受控 REST / UI 完成;不得在业务任务上试验。
|
||||
可用 `python scripts/test_gitea_claim_race.py --repo 【owner/repo】 --apply` 在唯一 `claims/__probe__/race-*` 临时分支做兼容性 smoke,期望恰好一个 `201`、一个 `409`。脚本只在名称前缀和 SHA 都符合预期时清理并复查 404。一次 smoke 结果不能证明 create-branch 线性化;无论结果如何,MVP 仍依赖 dispatcher 串行分配。不得在业务任务分支上试验。
|
||||
|
||||
## 自动化与升级阈值
|
||||
|
||||
- `python scripts/validate_harness_governance.py` 完全离线检查上下文清单、导航、本地链接、任务 frontmatter / 依赖 / 写路径、Gitea 模板和已跟踪文本中的敏感值。
|
||||
- `.gitea/workflows/harness-governance.yml` 在 push / PR 运行标准库测试和离线检查,不注入本机长期 PAT,也不运行远端审计。平台仍会提供 job token,工作流用 `permissions: read-all` 和 `persist-credentials: false` 收窄权限与留存。
|
||||
- Actions 模板只有合入默认分支、仓库启用 Actions 且带 Python 3.10+ 的 `ubuntu-latest` runner 可用时才会真正执行;内网 runner 还要能取得 `actions/checkout@v4`。没有 runner 时,以相同本地命令作为验收证据,不宣称 CI 已跑绿。
|
||||
- 退出码统一:`0` 通过,`1` 发现一致性问题,`2` 配置、网络或运行前提缺失。敏感信息检查只输出规则、文件和行号,不回显命中正文。
|
||||
|
||||
MVP 不实现 webhook、协调服务或独立 dashboard。只有出现以下任一信号才重新评估:单项目约 20 个以上并发任务、dispatcher 成为持续瓶颈、跨仓库聚合成为刚需、重复出现路径分配竞态,或审计 / 合规要求集中查询。届时优先增加原子 allocation 服务和 webhook 索引,再评估只读 dashboard;不把前端看板当作并发控制器。
|
||||
|
||||
Reference in New Issue
Block a user