Files
knowledge-governance/docs/architecture/three-agent-handoff-workflow.md
T
姚章浩 c1d49adb62
build / build (push) Successful in 53s
docs(agents): 补三Agent交接闭环工作流(状态机、快照tag与验收模板)
工单 #6。三个会话(架构/开发/验收)在同一仓库串行协作时缺一份可核对的
书面契约,口头约定会漂移:状态标签命名不统一(仓里还留着 status/working)、
快照 tag 被当正式版本用、验收拿开发总结当结论。

本文件把契约写死:角色边界、main 单线串行、六个状态名与 blocked 要素、
快照 tag snapshot/vX.Y.Z 与正式版本 vX.Y.Z 分线、不可变与回退规则、
开发与验收交接模板、关单条件、安全边界。

为什么快照不能沿用 vX.Y.Z:make verify 里的 check_changelog --tags 会对每个
正式 tag 断言其提交内 CHANGELOG 顶部版本一致;在临时仓库实测正式 v0.1.1 与
v0.1.2 都判红,而 snapshot/v0.1.1 被归为非正式 tag 通过。故快照改用
snapshot 前缀,正式版本仍走单独批准与 CHANGELOG 流程。

范围:仅新增这一个文档,不改 CHANGELOG 与 CI。
验证:make verify 15 项全 success(档案 local,退出码 0)。
2026-10-14 17:33:22 +08:00

160 lines
9.8 KiB
Markdown
Raw 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.
# 三 Agent 交接闭环工作流(架构 / 开发 / 验收)
> 适用仓库:`Weknora/knowledge-governance`(默认分支 `main`)。
> 本文件是**交接契约**,不是介绍:它规定角色边界、状态机、版本口径与交接证据格式,
> 供三个独立会话(架构 / 开发 / 验收)在同一仓库上串行协作时逐条核对。
> 依据:工单 `#6` 的「文档内容合同」8 条(正文=目标与范围,评论=证据,commit/tag=版本)。
## 0. 与工单合同条目的对应(便于独立验收逐条核对)
| 合同条目 | 本文位置 |
|---|---|
| 1 三角色职责 | §1 |
| 2 main 单线、串行修改 | §2 |
| 3 六个状态名、标签互斥、blocked 要素 | §3 |
| 4 每轮可交接的修改链路(自测→commit→快照 tag→推送→核实→review) | §4、§5 |
| 5 不可变与回退规则 | §5 |
| 6 开发/验收交接模板 | §6 |
| 7 关单条件 | §7 |
| 8 安全与边界 | §8 |
## 1. 三角色职责
| 角色 | 能做 | 不能做 |
|---|---|---|
| **架构** | 需求、设计、把目标/范围/验收标准写成工单;维护工单正文与验收矩阵;写设计文档 | 不写业务代码、不改测试/依赖/构建配置、不推送、不启动其他 Agent |
| **开发** | **唯一**可以改业务代码、测试、依赖、迁移、构建配置与项目文档的角色;自测、commit、打快照 tag、推送 main 与 tag | 不设置 `accepted`、不关单、不改验收标准、不部署 |
| **验收** | 独立复核差异与版本信息,逐项判定 PASS/FAIL/BLOCKED;通过后置 `accepted` 并关单 | 不修复代码(发现问题置 `rework` 并把问题列清)、不自行 `checkout`/`reset` |
| **prototype(UI)** | **仅**当工单涉及 UI 原型时介入,产出可打开的原型与交互证据 | 不参与非 UI 任务,不代替开发提交业务代码 |
角色约束是**流程约束**,不是技术 ACL 隔离:本机共用 Gitea 管理员凭据,谁也拦不住谁越权,
所以每个角色都要靠"读回核实"而非"相信对方自述"来交付。
## 2. main 单线、单仓库串行
- 只使用 `main` 一条线:**不创建分支、不建 worktree、不走 PR、不做合并**(通用引擎规范里 PR 是可选姿态,
本仓沿用"以直推为主"的口径)。
- 同一仓库的代码修改**串行**:一个工单在主开发阶段只由一个开发会话持有。
- **review 期间开发停止修改代码**:工单进入 `status:review` 后,除按 `rework` 要求修复外不再改动。
- 切换现场前先取证:`git status --short --branch`、`git worktree list`、`git rev-parse origin/main`。
工作区不干净、或存在不属于本工单的改动时**不得混入**,安全处理不了就置 `status:blocked`。
- 别人的 feature 分支与成果原样保留:不 `reset`、不 `stash`、不 cherry-pick、不清理旧分支/worktree/tag。
## 3. 工单状态机
六个完整状态名,**互斥**,同一工单最多一个状态标签:
```
status:ready → status:developing → status:review → status:accepted
↑ │
└── status:rework ─┘ status:blocked(可从中途进入,解除后回原状态)
```
| 标签 | 含义 | 进入条件(缺一不可) |
|---|---|---|
| `status:ready` | 设计完成、用户已批准开发 | 用户明确批准;范围与验收标准写明 |
| `status:developing` | 开发已领取并在实施 | 评论记录工单号、起始 SHA |
| `status:review` | 已交付待独立验收 | commit/tag/推送/远端核实/交接证据齐全 |
| `status:rework` | 验收失败 | 验收列出明确不通过项(不授权扩范围) |
| `status:accepted` | 指定版本通过验收待核实交付 | 验收矩阵逐项通过,结论绑定 tag + 完整 SHA |
| `status:blocked` | 受阻停手 | 有原因、处理人、解除条件、原状态;解除后**恢复原状态** |
- `Closed` 是 **Issue 状态**,不是标签:通过验收后由验收移除状态标签并关闭。
- 未批准的工单保持 Open 且**不打** `status:ready`。
- 标签 ID **按仓库实时读取**(`GET /api/v1/repos/{owner}/{repo}/labels`),不跨仓库复用 ID。
- ⚠️ Gitea 1.26.2 实测:`PATCH /issues/{n}` 带 `labels` 返回 201 **却不改任何标签**(静默无效);
替换标签必须用 `PUT /issues/{n}/labels`,且每次写完 `GET /issues/{n}` **读回核验**。
- ⚠️ 本仓历史遗留标签 `status/working`(`/` 而非 `:`)不符合本约定,遇到时应归一为 `status:developing`。
## 4. 快照 tag 与正式版本分线
**两种版本语义必须分开**,混用会让 CI 变红或让快照被误当发布。
| | 快照(开发交付用) | 正式版本(发布用) |
|---|---|---|
| 命名 | `snapshot/vX.Y.Z` | `vX.Y.Z` |
| 类型 | **注释 tag**(`git tag -a`) | **注释 tag**(`git tag -a`) |
| 批准 | 开发按工单直接创建,序号递增 | **用户单独批准** |
| 与 CHANGELOG 的关系 | 无要求;快照序号独立于构建显示版本,**不要求二者相等** | 必须同步写 `CHANGELOG.md` 顶部该版本段 |
| CI 门禁 | 不被当作正式版本 | 受版本一致性门禁约束 |
| 含义 | 代码快照,**不代表验收、不代表发布** | 发布事实,需 Release 等后续动作 |
**为什么不能用 `vX.Y.Z` 当快照(实测结论,勿凭直觉改回)**
- `make verify` → `scripts/check_changelog.py --tags`(`Makefile:27`、`scripts/ci_checks.py:254`)对每个
`vX.Y.Z` tag 断言:tag 指到的提交里 `CHANGELOG.md` 顶部正式版本 == tag 版本,且必须是注释 tag
(`scripts/check_changelog.py:44-45,257-278`)。
- CI 检出会把 `git ls-remote --tags origin` 落成 `.ci-remote-tags` 并 `fetch --tags`(`.gitea/scripts/ci_checkout.sh:122,127`);
`CI=true` 时拿不到清单直接判红 ⇒ **不带 CHANGELOG 段的正式 tag 会让 main 从此每次推送都红**。
- 正式 tag 的匹配是 `^v(\d+\.\d+\.\d+)$`(`scripts/check_changelog.py:45`),`snapshot/v0.1.1` **不匹配**,
因此不参与版本一致性判定;`scripts/version.py:119-151` 的发布证据也只认 `refs/tags/v<版本号>` 的注释 tag
与 CHANGELOG 顶部一致 ⇒ 快照 tag 不会把构建身份变成 release。
- 结论:快照一律用 `snapshot/vX.Y.Z`;要发正式版就单独走批准 + CHANGELOG 流程。
- 修复后的下一快照序号递增(`snapshot/v0.1.2`),**不得移动已交付的快照 tag**。
## 5. 每轮可交接的完整链路 + 不可变性
一轮可交付的修改必须走完整条链,任何一步失败都要如实报告**实际状态**,不能假装完整交付:
```
自测(真实命令 + 真实结果)
→ commit(信息含工单号;不用 Closes/Fixes/Resolves 等自动关单关键词)
→ 创建递增的不可变快照 tag(注释 tag)
→ 正常推送 main 与该 tag
→ 远端核实(main 含结束 SHA;tag 解引用后指向结束 SHA)
→ 工单评论完整交接证据 → status:review
```
- 开发的**正常 main 与快照 tag 推送无需逐次确认**;但**部署与正式发布不在授权内**。
- 推送前必须先核查:目标名未被占用、推送是否会触发生产部署(`.gitea/workflows/` 里是否有 deploy 作业)。
本仓唯一工作流 `build.yml` 头部明确"只做 CI,不做 CD"(`on: push: branches: "**"`)⇒ 普通 CI 允许放行。
- **保留旧提交与旧 tag**:不强推、不覆盖、不删除旧 tag、不改写历史。
- 回退只能由开发执行 `git revert` 并创建**新的**版本(`revert` + 递增快照),旧提交与旧 tag 原样留着。
- CI 绿 ≠ 已部署;`main` 不是自动可发布版本。
## 6. 交接与验收模板
### 6.1 开发交接(写进工单评论)
| 字段 | 内容 |
|---|---|
| 工单 | 编号 + 标题 |
| 起始 SHA | 动手前的 `origin/main` 完整 SHA |
| 结束 SHA | 本次提交的完整 SHA |
| tag | 快照 tag 名 + 类型(注释)+ 解引用后的 SHA |
| 实现摘要 | 改了什么、为什么这么改 |
| 文件清单 | `git diff --stat` 的范围,逐条列出 |
| 测试命令与结果 | **真实执行的命令 + 真实输出**(不可只写"已测试") |
| 已知问题 | 未验证项、环境限制、待批事项 |
| 剩余完成项 | 明确列表;没有就写"无" |
### 6.2 验收结论(写进工单评论)
| 字段 | 内容 |
|---|---|
| 版本锚点 | tag + 完整 SHA(结论必须绑在这上面) |
| 逐项判定 | 合同/矩阵每项 `PASS` / `FAIL` / `BLOCKED`,FAIL 必须列明确问题 |
| 复验命令与结果 | 独立跑过的只读检查(如 `git diff --check`、远端 tag 读回)与真实输出 |
| 问题与结论 | 通过 ⇒ 下一步;不通过 ⇒ 置 `rework` 并把问题列清;无法验证 ⇒ 置 `blocked` |
验收不得凭开发总结宣告通过;也不得自行 `checkout`/`reset` 去凑现场 —— 现场不对就要求开发准备。
## 7. 关单条件
同时满足才可关单:
1. 验收矩阵全部通过;
2. 目标版本可追溯(tag + 完整 SHA 可读回);
3. 远端交付已核实(远端 main 含结束 SHA,tag 解引用一致);
4. 交接与验收文档齐全(含真实执行证据);
5. 无未解除的阻塞项。
**不需要** PR、不需要合并、**不默认需要部署**。
## 8. 安全与边界
- 不提交任何密钥/隐私:决定只用 secret 文件(仓库外、0600),日志与评论不出现秘密与正文原文。
- 每个外部写操作(标签、评论、tag、推送)都**读回核实**;结果不明确时不得宣布成功。
- 不使用自动关单关键词,不绕过独立验收。
- 角色约束不是技术隔离;`main` 也不是自动可发布版本。
- 每次执行开始先确认:仓库、路径、工单、当前状态、最新评论、授权;缺一项且无法查询就先问,不猜。