c1d49adb62
build / build (push) Successful in 53s
工单 #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)。
9.8 KiB
9.8 KiB
三 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.Ztag 断言: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. 关单条件
同时满足才可关单:
- 验收矩阵全部通过;
- 目标版本可追溯(tag + 完整 SHA 可读回);
- 远端交付已核实(远端 main 含结束 SHA,tag 解引用一致);
- 交接与验收文档齐全(含真实执行证据);
- 无未解除的阻塞项。
不需要 PR、不需要合并、不默认需要部署。
8. 安全与边界
- 不提交任何密钥/隐私:决定只用 secret 文件(仓库外、0600),日志与评论不出现秘密与正文原文。
- 每个外部写操作(标签、评论、tag、推送)都读回核实;结果不明确时不得宣布成功。
- 不使用自动关单关键词,不绕过独立验收。
- 角色约束不是技术隔离;
main也不是自动可发布版本。 - 每次执行开始先确认:仓库、路径、工单、当前状态、最新评论、授权;缺一项且无法查询就先问,不猜。