Files
DevFlow/docs/02-架构设计/规格契约自检机制-2026-06-14.md
绝尘 f3c0967f41 修复: AR-3 审批 reason 查项目名拼对象名(用户再反馈 P0)
build_approval_reason 改 async + 接收 db,对 delete/restore/purge/update/bind/create_task 的 id/project_id 查 ProjectRepo.get_by_id 拼「项目名」(id=x)(原只拼裸 id,用户反馈'只返回 ID 不知道是什么数据')
新增 resolve_project_label helper;process_tool_calls 调用改 await
来源 aichat审查报告 第二章 + 用户 2026-06-14 再反馈;cargo 0 err
2026-06-14 17:16:28 +08:00

15 KiB
Raw Blame History

规格契约自检机制

创建2026-06-13 | 阶段:设计定稿,待落地 性质:设计说明 + 可执行规格基准。后续 spec-verifier agent、/spec-check skill、自检 hook 均从本文档推导。


0. 背景与问题

全程 AI coding 下,开发节奏快(实测 ~4 Sprint/天),产生两个痛点:

  1. 规格无锚点 → 漂移 → 不敢当契约用:写下的 spec 没人验证,与代码逐渐脱节,最终失去参考价值。
  2. done/todo/decision 散落 → 记不住:做了什么、没做什么、做了哪些决策,事后查不清。

错误方向:新建独立的「需求规格」文档。静态 spec 必漂移;本项目无外部契约/验收需求spec 的核心价值(沟通契约/验收基准)不成立;独立文档违反 SSOT成为第三处真相源。

正确方向活契约living contract——契约跟决策一起演进,由 AI 自检维持与代码一致。不新建文档,改造现有功能决策记录。


1. 核心方向:活契约

契约不单独成文,而是挂在功能决策记录的每条决策上。一条 ✅ 已落地 的决策,就是一条当前生效的契约。

决策记录(活契约载体)
  ├─ 决策三要素:决策 / 原因 / 状态
  ├─ 代码锚点:让契约可被验证(机制 A
  └─ 状态字段:聚合出完成度(机制 B
        ↓
AI 自检维持契约与代码一致(机制 C/D/E

2. 机制 A代码锚点防漂移

✅ 已落地 的决策加一个代码锚点。锚点以符号名 + grep 关键词为主锚(跨修改稳定),行号为辅锚最近定位可漂AI 自愈)。规格真相留在代码里,文档只留索引 + 意图。

形态

### connect_timeout 不设总 timeout [Sprint 6]
- 决策reqwest Client 加 connect_timeout(30s),不设总 timeout
- 原因:连接阶段防无限 hang总 timeout 会误砍流式长生成任务
- 状态:✅ 已落地
- 锚点:符号 `Client::new` | grep `connect_timeout` @ client.rs:142行可漂AI 自愈)
- 自检PostEdit 触发锚点一致性 | 上次2026-06-13 ✅
  • 锚点:符号 + grep 为主真相行号为辅快照。人补一次AI 维护行号。
  • 自检行AI 验证后回写时间戳与结果,人扫一眼即知近期是否验过。

主辅分明:为什么行号不是主体

锚组成部分 稳定性 角色
符号名(函数/类/常量) 高(重构才改) 主锚
grep 关键词 中高 主锚(定位调用点)
行号 低(加删几行就漂) 辅锚(最近定位,可漂)

代码高频修改下,行号必然漂;行号作主体 = 锚点必然失效。符号 + grep 才是跨修改稳定的真相。

三层抗漂

  1. 行号漂(最常见)grep 不受影响重新定位。AI 自愈(行号漂但 grep 在附近 → 自动修);机制未跑时 grep 命中也能秒级定位。人无感。
  2. 符号/grep 漂(罕见,如重命名):必伴随决策变更 → grep 失效 = 正确的报警信号,触发决策同步(命中第 5 节"信息不足"上报条件)。
  3. 功能删除grep 全失效 → 报警 → 人确认废弃或误删。

维护靠 AI 不靠人:人只在新增决策时补一次锚点;之后行号漂由 AI 在自检环节自愈,人改代码时无需动锚点。

状态阀门:锚点只绑稳定态

锚点验证只对相对稳定的契约有效。剧烈重构期契约本身不稳,强行锚是噪音。

状态 是否锚 原因
✅ 已落地 锚定 稳定,可验证
🚧 待实测 不锚/暂锚 不稳定,重构中
📐 设计未实施 不锚 未实现,无代码可锚

重构完成、代码稳了,🚧→✅ 再锚定。状态字段是防锚点失效的阀门。


3. 机制 B完成度聚合表

完成情况已编码在状态字段里(/🚧/📐)。缺的是按状态聚合的视图。在功能决策记录头部维护一张索引表:

## 完成度总览

| 状态 | 数量 | 代表条目 |
|------|------|----------|
| ✅ 已落地 | 38 | connect_timeout、provider 路由、知识库 Tier 分层 |
| 🚧 待实测 | 5 | 审计回写、… |
| 📐 设计未实施TODO | 12 | 向量检索、count_any 否定检测、IdeaPromoter 接线 |
  • = 做了什么;📐 = 没做什么;决策条目 = 做了哪些决策。三问一表答完。
  • 增量维护:新增决策更新计数;📐→✅ 迁移挪列。按状态聚合,不按时间,比 PROGRESS 流水好查。

4. AI 自检:三级验证

层级 验证内容 可靠性 谁验
L1 锚点存在性 文件:行 + grep 关键词命中 高(确定性) 主代理 grep
L2 取值一致性 具体数值/标志是否如 spec 所述 ⚠️ 中(范围窄,误读低) 主代理读码
L3 行为契约 代码逻辑是否遵守 spec 意图 低(主观) 子代理最小上下文

核心贡献AI 把脆弱的行号锚点变成自愈的语义锚点——

  • 行号漂但 grep 关键词在附近 N 行 → AI 自动修正锚点行号(可逆,自处理)。
  • 关键词消失 → 真报警,语义变了,需人决策。

5. 分流规则AI 能做 vs 人必须做

目标:让 AI 机械吞掉确定性/低风险/可逆的 80%,只把真正需要人脑的推到人面前。

5.1 两轴判定矩阵

客观唯一(确定) 主观/多解
只读/可逆 AI 全权自处理 ⚠️ AI 给候选 → 人定
有后果/不可逆 ⚠️ 报告 → 人定 🔴 必须人定

5.2 两条机械判定

AI 自处理(不报人)的充要条件确定性 = 客观 AND 动作 = 可逆。 (锚点行号自愈、数值核对一致、自检时间戳回写。)

必须上报人的条件(任一命中即报)

  1. 主观——验证答案不唯一(行为契约、意图符合性)。
  2. 有后果——动作不可逆(改决策语义、标记契约被破坏、回滚代码)。
  3. 信息不足——AI 无法判定(锚点关键词消失,但不知是否故意改的)。

5.3 兜底安全阀

规则未覆盖的场景,默认上报人,不擅自自处理。宁可多报,不可漏报关键。

5.4 高后果判定(决定是否触发子代理)

判为高后果(满足任一)
数据完整性 写库、迁移、状态机流转
并发安全 锁、共享状态、异步竞态
安全 鉴权、注入、凭据处理
外部契约 API/协议、第三方对接
不可逆操作 删除、覆盖、发布

高后果决策即使主代理自检报绿,仍触发子代理第二意见。


6. 反馈规格:五字段决策单元

上报给人的每条,必须是可点的闭合决策不是要调查的谜题。AI 把上下文打包进去,人只回答 yes/no 或选 A/B。

🔴 [决策名] connect_timeout 不设总 timeout
锚点client.rs:142 | 实际:行号漂至 158且 grep "timeout" 消失
证据:预期 .connect_timeout(30s) 无 .timeout() | 实际代码已加 .timeout(60s)
为何上报:命中「信息不足」——无法判定是故意改回总 timeout还是误改
候选A. 故意改 → 更新决策记录(状态/原因)
      B. 误改 → 回滚代码AI 推断 B 更可能:总 timeout 会误砍流式)
需你定A 还是 B

「为何上报」显式化分流规则,使判定可审计。


7. 子代理隔离验证L3 / 高后果层)

7.1 为什么用 agent 不用 skill

Skill Agent
上下文 复用主对话,不隔离 独立窗口,隔离
偏误 主代理带作者偏误执行(白搭) 消除作者偏误

主代理验证有结构性确认偏误:决策是它记的、代码是它改的,倾向支持自己对。隔离验证必须 agent。

7.2 零上下文 → 最小必要上下文

完全零上下文是双刃剑:子代理无领域知识会误判(局外人偏误)。正确形态是给事实,不给立场——子代理是陪审员,只看证据下判断。

卷宗格式(由编排方构造,传入 agent

断言:此函数应"丢弃残缺响应,不入库"
证据:<精确代码片段>
任务:判断代码行为是否符合断言

不给:决策原因字段、对话历史、是否刚改的、当初怎么定的。

7.3 分歧才报人

子代理不替代人,是在「上报人」前加第二意见:

主代理自检(带上下文判一次)
  ├─ L1/L2 确定性 → 自处理
  └─ L3/高后果 → 起 spec-verifier agent最小上下文判一次
        ├─ 两代理一致(都绿/都红)→ 按结论走
        └─ 两代理分歧 → 🔴 报人(分歧暴露主观性,只有人能定)

人的事件面从「所有主观项」压缩到「主观项中的分歧项」。


8. 触发环节:何时拉起 Agent

起 agent ⟺ 命中下列环节之一 AND 验证项是主观层(L3)或高后果。

环节 时机 起 agent? 验证范围 频率控制
A 改代码 PostEdit hook改到挂锚文件 改到高后果锚点才起L1/L2 主代理自验 仅被改那条契约 每次相关编辑,单条
B 回合结束 Stop hook降频 本回合涉及的高后果/主观项 本回合动过的 抽样≥10 轮/≥10 min
C 主动审计 手动 /spec-check 全部 L3/高后果 所有 决策 人触发,全量并行
D 记录决策 decision-record 标 /演进时 新记或 📐 的高后果项 该单条 每次 迁移
  • A 最值钱:在「可能制造漂移的时刻」拦截,单条,便宜。优先级最高。
  • B 兜底catch A 漏的(一处改多处)。
  • C 体检:清历史漂移,最贵,人触发。
  • D 防脱节:决策记了但代码没跟上, 迁移时必验。

全程 AI coding 下A/B 自动跑零摩擦C 人按需D 跟 decision-record 自然触发。无需人记「该验证了」。


9. 三层架构与组件骨架

hook时机→ /spec-check skill编排→ spec-verifier agent隔离验证
   PostEdit/Stop     读记录+分流+封装卷宗        最小上下文判定
                     L1/L2 自处理                 L3/高后果
                     收集+分歧上报                返回:判定+置信+分歧点

spec-verifier agent.claude/agents/spec-verifier.md

你是独立契约验证者。只依据调用方给你的【断言+证据】判断。
不假设意图,不参考对话历史,不信任任何"应该是什么"的预设。
输出:判定(符合/违反/无法判定)+ 置信度 + 关键分歧点(一句话)。
无法判定时必须明说,禁止凑结论。

/spec-check skill.claude/skills/spec-check/

1. 读功能决策记录,提取所有 ✅ 条目(锚点+断言)
2. 分流L1/L2确定性→ 自己 grep 验,自处理
         L3/高后果(主观)→ 调 spec-verifier agent传断言+代码片段,不传决策原因)
3. 主代理自己也判一次 L3带上下文
4. 比对:分歧项 → 按五字段格式化上报;一致项 → 按结论走
5. 锚点行号漂移 → 自愈(可逆,自处理)

基建复用devflow 已在用 hookStop 降频)+ skill/review、decision-record。三层机制全是同构基建不引入新依赖。


10. 落地顺序

  1. 本文档定稿(当前)——后续所有实现的规格基准。
  2. 功能决策记录瘦身 + 补锚点——删微决策膨胀730→~300 条目补代码锚点。
  3. 主代理自检 hook——PostEdit环节 A+ Stop 降频(环节 B覆盖 L1/L2 确定性层。
  4. spec-verifier agent + /spec-check skill——覆盖 L3/高后果四环节A/B/C/D主观层。
  5. 分歧上报机制——五字段决策单元,接入 Stop hook 通知。

11. 用户操作指南

本节是人视角的操作手册。机制细节见 2-9 节,这里只讲「你做什么」。

心智模型3 按钮 + 1 屏

整个机制里,人只做三件事,看一块屏。其余全是 AI 自动。

人的动作 时机
🔘 记决策 做取舍时,让 AI 用 decision-record 记下(决策/原因/状态) 每次开发有取舍
🔘 裁决分歧 AI 上报时,在候选里选 A 或 B 子代理与主代理打架时(偶发)
🔘 跑体检 执行 /spec-check 全量扫描 大版本前 / 重构后
🖥 完成度表 翻功能决策记录头部「完成度总览」表 想看进度时

人 vs AI 分工

动作 归属 频率
做开发取舍(选 A 不选 B 每次开发
记决策 + 补锚点 AI 做,人确认 决策落地时
维护锚点行号(自愈) AI 自动
验证代码符合契约 AI 自动
起 hook / 子代理自检 AI 自动
裁决 AI 分歧 上报时
查进度 人(看表) 随时

人只做两件:记决策 + 裁决分歧。验证、维护、检查全归 AI。

看的入口与时机

想知道 看哪里 时机
做了/没做/做了哪些决策 功能决策记录头部「完成度总览」表 随时
AI 发现的契约冲突 上报条目(五字段:锚点/证据/为何上报/候选/需你定) 被动收(偶发)
全量漂移体检 /spec-check 红项报告 主动(大版本前)

做的节奏

  • 记决策:开发中一有取舍,当场记。齿轮转起来的起点,零额外成本。
  • 裁决:收到上报 → 选 A/B → AI 执行。
  • 体检:每 Sprint 末或重构后跑一次 /spec-check,清历史漂移。
  • 迭代机制:规则不准(误报/漏报)→ 改本文档第 5 节分流规则。机制文档是活的。

一天的工作流

开发中做取舍 ──→ 🔘记决策AI 补锚点,人不管)
       │
       │  AI 后台hook 自检 / 子代理验证 / 行号自愈
       │
   AI 打架?─是─→ 🔘裁决(选 A/B
       │否
       ▼
  想看进度 ────→ 🖥翻完成度表
       │
  大版本前 ────→ 🔘跑 /spec-check 体检

当前态:能做什么

机制尚未落地(落地链 ②-⑤)。当前能力边界:

能力 现在 建成后
🔘 记决策 已有decision-record
🖥 完成度表 需先补锚点 + 建表(②)
🔘 裁决上报 需 ③④⑤
🔘 /spec-check 需 ④

解锁其余能力的起点是落地链 ②:补锚点 + 建完成度表。


12. 诚实边界

  • AI 自检降低漂移,不消除。 行为级契约仍需人盯。别因「AI 验过」就放心改语义。
  • 同一 AI 的盲区贯穿写与验。 全程 AI coding 下,当初记录漏掉的约束,验证时 AI 也想不到查。关键决策的 spec人过一眼。
  • 一致 ≠ 正确。 两代理一致时仍可能共享同一盲区spec 本身写错,两代理按错的理解一致)。一致只代表「无分歧可上报」。
  • selective 用子代理。 全用 = 成本爆炸。只 L3 + 高后果。