# 规格契约自检机制 > 创建:2026-06-13 | 阶段:🗄️ 归档不实施(2026-06-28 核验:0 行代码。其核心价值「规格与实现一致性检查」已被 L1 求助协议 + ai_self_review gate 覆盖,过度设计) > 性质:设计说明 + 可执行规格基准。后续 `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 自愈)。规格真相留在代码里,文档只留索引 + 意图。 ### 形态 ```markdown ### 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:完成度聚合表 完成情况已编码在状态字段里(✅/🚧/📐)。缺的是按状态聚合的视图。在功能决策记录头部维护一张索引表: ```markdown ## 完成度总览 | 状态 | 数量 | 代表条目 | |------|------|----------| | ✅ 已落地 | 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。 ```markdown 🔴 [决策名] 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 已在用 hook(Stop 降频)+ 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 + 高后果。