应用篇 12. 生产 SDD 反模式:应用周期的诊断地图
状态:建议。 本篇汇集了恰恰出现在应用周期中的反模式:决斗、文件仲裁、Spec CI、分层预算、anti-Goodhart 指标以及自动修复。它们延续了第一卷第 20 篇的路线:制品存在,检查存在,代理运行迅速,但对系统的控制正在逐渐丧失。
本篇作为应用卷的诊断地图。如果生产回路变得嘈杂、矛盾,或以牺牲用户为代价保护自身速度,请从这里开始。
阅读前
- 来自第一卷的支撑:第 20 篇展示了 SDD 的基本反模式。
- 本地学习案例:来自第 8–11 章的任何已创建制品。
- 用于
capstone/的痕迹:所选high_memory_usage包的blocker / owner / next_check三行。 - 首读的主要术语:诊断阻塞项。各个反模式的名称仅供参考,无需连续阅读。
- 可推迟的事:将每个反模式转化为自动 CI 策略。
目标
本章的目标不是记住一份名称清单,而是对已收集的生产 SDD 包进行一次简短的审计。本章结束后,你应该拥有三行诊断:什么阻塞了准入,谁负责修复,以及何时再次检查。
首读时,请将本章作为检查清单,而非所有可能故障的字典。下面的目录用于识别已发现的问题;可以用 blocker / owner / next_check 三行收尾。
第一卷反模式的升级
本章目录中的一部分反模式并非新出现的,而是第一卷第 20 篇中所述内容的生产版本。差别在于规模:在学习周期中,基础反模式会浪费一天的工作;在应用周期中,它会打开线上服务的一整类事件。
| 本章的反模式 | 来自第一卷的升级 |
|---|---|
| 章程流于形式 | "无人打开的章程"(第一卷,第 20 篇) |
| 制品中无 diff 的有毒规范 | "代码之后的规范"——打解释的补丁而非修改制品 |
红色 CI 后的 validation.md 漂移 | "错误后的事实弱化"(第一卷,第 20 篇) |
| 无复审期限的影子规范 | "QWEN.md 沦为垃圾场"——没有 ttl 和作者的备忘 | | 无证据标记的痕迹 | "口头事实"——缺少 evidence_ref |
如果你已经学习了第一卷第 20 篇,这些条目可以有选择地阅读——这里只添加生产上下文。本章其余的 12 个反模式仅出现在应用周期中(决斗、仲裁、预算、anti-Goodhart、自动修复),在第一卷中不作讨论。
最小学习场景
学习案例
在最终考核前,需要检查一个生产 SDD 回路的噪声。从第 8–11 章中任选一个包:judgment.md、validation.md、budget_network.yaml 或 readiness 表。目标是找出不少于三个反模式,或明确证明它们不存在。
准备
- 下方的诊断检查清单。
book2/examples/templates/retrospective.md——简短记录输出的表单。- 一个已经通过可运行示例或手动学习场景的制品。
步骤
- 选择一个制品,不要扩大检查范围。期望:审计耗时 15–30 分钟,而非变成对整个项目的审查。
- 回答检查清单中的 12 个问题。*期望:每个答案为
yes、no或not_applicable,并附简短的文件链接。* - 对每个
no,指出反模式、所有者和修复期限。 - 在所选章节中至少找到一个
[project script],并检查它是否被标记为可运行类比或"自行实现"接口。 - 将结论记录到
antipattern-audit.md或回顾中:什么阻塞了生产准入,什么可以作为改进保留。
检验事实
存在一个三项清单:blocker、owner、next_check。如果否定回答只被转化为笼统建议,则诊断地图未能完成其职能。
它如何进入 capstone/
将 blocker / owner / next_check 三行移入 capstone/antipattern-audit.md。不要在没有单独记录的情况下在同一文件中修复这些问题:对考核而言,重要的是看到诊断和下一次检查,而不是一份没有历史的漂亮总结。
最小片段:
| blocker | owner | next_check |
|---|---|---|
| readiness 缺少 `evidence_ref` | platform | 用 fixture 链接重做 dry-run |
| `[project script]` 没有可运行类比 | devex | 替换为 `examples/real-api` 或实现脚本 |
| 未指定 `manual_review_floor` | sre | 在 auto-mode 之前添加 guard 指标 |
可审查的痕迹
如果审计涉及学习包或 capstone/,请保留 antipattern-audit.md。不要在没有单独记录的情况下在同一变更中修复发现的问题:先看到诊断,然后才是修复。
关键思想
下面的每个反模式按相同的三字段模式分析:症状——在制品和日志中观察到的情况,为何有害——如何改变团队或代理的行为,如何修复——使症状可被自动捕获的最小步骤。
目录可以按任意顺序阅读:每个条目独立,仍遵循相同的三字段。这些条目并非互斥——它们是经常一起出现的不同噪声类别。如果在一个生产包中发现了三个或更多反模式,请将它们视为一个相互关联的问题网络,其共同根源在于丢失的风险契约。
示例与应用
章程流于形式
> 第一卷反模式的升级。 基础版本是第一卷第 20 篇中的"无人打开的章程"。在应用周期中,同一错误不会导致糟糕的代码,而是导致执行了危险的操作。
症状:仓库中存在带有 immutable_principles 和 mutable_rules 的 constitution.md,但危险操作前的网关并未启动。在 judgment.md 中有对 forbid_unscoped_delete 规则的引用,在团队日志(logs/auto-remediation.jsonl)中没有一次调用 scripts/constitution/check.py。过去 30 天内,网关的触发次数为零。
为何有害:规则形式上存在,但在压力时刻并不约束代理。经历几次事件后,团队开始将章程视为装饰。在下一次事件的复盘会上会出现这样一句典型的话:"规则本来是有的,只是没人检查"——这是章程作为注释而非契约运作的信号。
如何修复:
- 在执行 playbook 之前将
constitution.md接入网关检查(参见第 3 篇); - 任何危险操作都需通过
scripts/constitution/check.py或 CI 中的等价物;
- 在
judgment.md中记录章程版本和decision_hash的引用; - 如果规则未被自动检查,则将其从
immutable_principles移入 playbook,并明确标记为"指令,非网关"; - 检查过去 30 天内是否至少有一条日志显示网关触发并阻止了某个操作。一个季度内零触发,要么是章程未接入,要么是它在描述不存在的风险。
带 ttl: ∞ 的可变规则
症状:mutable_rules 中存在没有 ttl 的规则,或 ttl 设为数年。rollback_condition 缺失,或被表述为"由团队决定"。
为何有害:规则无限期存活,并逐渐变成不变式的隐藏部分。一年后,参与者已不记得它为何出现,并害怕修改它。该规则的修正被类比地应用于原本未曾设想的情况。
如何修复:
- 以天为单位设置
ttl,而非年;首次复审为 30–90 天(参见INSTRUCTOR.md中的参考答案); - 将
rollback_condition表述为可验证的谓词:repeat_incidents_same_node>=2、silent_p0_ratio>0.05、safety_veto=true;
- 在
ttl到期后,暂停该规则的适用,直至通过公投明确续期; - 删除在其整个生命周期内从未触发过的规则。
制品中无 diff 的有毒规范
> 第一卷反模式的升级。 基础版本是第一卷第 20 篇中的"代码之后的规范":解释在变,而制品未变。在反向任务(恢复规范)中,同样的错误表现为:补丁治标不治本。
症状:团队在有毒规范上训练规范恢复能力(参见第 2 篇),但补丁只修改了解释文本或注释。requirements.md、plan.md、validation.md 保持原样。
为何有害:练习不再教授定位根本原因。一周后,同一类有毒规范再次通过——补丁并未封堵根本原因。
如何修复:
- 在课程的完成标准中要求至少在一个制品(
requirements.md、plan.md、validation.md、constitution.md)中产生 diff——而不仅仅在解释中; - 修复后进行完整的反向回放
Specify → Plan → Tasks → Implement;
- 如果补丁只修改了解释文本,则要求重做;
- 将受控缺陷规范的类别登记到
precedents.md,以便后续类似缺陷可被自动捕获。
以谨慎为借口的 ask_storm
症状:代理在循环中不断提出澄清性问题而不转向解决方案。判定串:cycle_count > 0 && ask_storm >= 4 && escalation_path_resolved=false(ask_storm——无新数据时重复澄清的计数器)。
为何有害:问题看似谨慎,但实际上是规范内部矛盾的信号。人类每次临时回答都添加了一个新的措辞,但该措辞未在任何地方固化,并在下一次 /clear 时消失。
如何修复:
- 在连续三次澄清后停止会话,并检查
requirements.md是否存在矛盾; - 将规范视为有毒的(第 2 篇)进行剖析——单一缺陷,查找根本原因;
- 将回答固化在
requirements.md或clarifications.md中,而非聊天里; - 不要将代理的提问变成持续对话:每次澄清要么关闭一个规范条目,要么回到规范修订。
无明确原因的 stage_regress
症状:implement 阶段返回到 plan,plan 阶段返回到 specify,但没有原因记录。第二天没人记得为何重写了计划。(stage_regress——无明确原因地回退到 SDD 循环的上一阶段。)
为何有害:SDD 循环变成漂移。每一次回退都会丢失前一步的上下文,一周后项目会有三份半成品的计划草案,没有一份在 validation.md 中被事实闭环。
如何修复:
- 明确地记录阶段之间的转换:可审查的变更记录、
genealogy.md中的记录、原因; - 禁止在不更新
validation.md中至少一个事实的情况下回退到上一阶段; - 使用项目技能,在
git status时显示当前阶段及尚未闭环的事实; - 如果回退频率高于每天一次,这表明规范太少,而非流程在制造噪声。
阶段间的 phase_context_loss
症状:specify 确定了决策,plan 未能继承,implement 始于一份从未经过 validation.md 的草稿。(phase_context_loss——阶段之间上下文的丢失。)
为何有害:每一步都基于自己的世界图景运作。经过两三次转换后,它们分歧过大,制品互相矛盾,任何检查都能平凡通过——因为它检查的并非真正需要检查的内容。
如何修复:
- 阶段之间只传递文件链接(
@specs/...),而非聊天中的转述; - 引入项目技能
check_phase_handoff,检查计划是否引用了现行的requirements.md,而实现是否引用了现行的计划; - 在
/clear后,开始新阶段时先阅读QWEN.md、最新的requirements.md和当前的plan.md; - 如果一个阶段无法说明它实现了上一阶段的哪个条目,则在修改代码前应回到上一阶段。
验证者沦为普通的代码审查
症状:验证者写出诸如"风格不太好"、"最好重命名"、"我们讨论一下"之类的评论。没有具体的反例、规则引用或 JSON Schema 违规。
为何有害:决斗失去了程序性。验证者不再是正式回路,而只是另一种意见。实现者以自由文本回应,争论转移到聊天中,无法复现。
如何修复:
- 禁止验证者在没有具体制品的情况下作出任何判断:带最小化条件的反例、hook 日志、JSON Schema、Given/When/Then;
- 裁定结果应可在本地复现:另一个人基于相同的
cases/和metrics/应得到相同的judgment.md; - 如果验证者未找到反例,则记录
verdict=APPROVE并继续推进,而非以笼统措辞继续讨论; - 将风格性意见移至单独的审查通道,不要与决斗混在一起。
仅由多数票决定的文件仲裁
症状:governance_protocol 被描述为"3 人中 2 票赞成",没有 Safety 的 veto 和平局裁决机制。票数相同时,系统会卡住或根据召集日期作决定。
为何有害:Safety 角色失去意义。决定由验证者和实现者投票作出,他们都在优化速度;关键风险被当作"可接受"通过。
如何修复:
- 为 Safety 角色引入
safety_veto: critical_risk; - 设置
tie_breaker: safety_first_then_latest_matching_precedent; - 由 Spec CI 网关检查
governance_protocol:缺少 tie-breaker 和 veto 将阻塞 merge;
- 每一次因 Safety veto 而产生的偏离都应记录在
precedents.md中,并附上对不可变规则的引用,以便类似争议更快闭环。
虚假的 [project script]
症状:在规范、检查清单或教学章节中使用了形如 python3 scripts/spec_ci/check_scope.py 的命令,但仓库中并不存在该脚本。无人运行过它,"检查通过"是隐含的,而非观察到的。
为何有害:产生一种虚假的安全感。CI 看起来很严格,但检查并未实际执行。几周后,团队会忘记哪些脚本是真实的,哪些只是接口。
如何修复:
- 在每个
[project script]块旁明确说明examples/中是否存在可运行的类比,或这是"自行实现"的接口; - 在 Spec CI 中设置单独的步骤,检查
validation.md中提到的命令是否真实存在(test -x path/to/script); - 教学章节中仅对能够通过本地
python3 scripts/...的命令标记[runnable]; - 如果脚本需要但尚不存在,应创建一个具有明确实现日期的工单,而非留作"以后再说"。
没有配对反向指标的裸 KPI
症状:在 validation.md 中存在目标指标(MTTR<=5m、coverage>=80%、auto_close_rate>=0.9),但没有配对的控制指标(guard 指标)。当 KPI 达成时,CI 网关通过。
为何有害:典型的古德哈特效应。代理或团队学会不惜代价达成指标:将复杂事件当作简单事件关闭、将 P0 标记为 P2、绕过人工审查。指标上升,实际质量下降。
如何修复:
- 为每个目标指标配对一个 anti-Goodhart 指标(参见第 10 篇):与
MTTR配对的是silent_p0_ratio和manual_review_floor;与coverage配对的是mutation_kill_rate; - 仅当配对指标同时达成时,网关才放行;
- 任何阈值的变更都是风险事件,而非 YAML 的修饰性修改;应附带依据加以记录;
- 定期对历史事件进行重放:新版本不得在已分析案例的判定上出现退步。
红色 CI 后的 validation.md 漂移
症状:CI 失败后,拉取请求的作者修改了 validation.md 中的阈值或删除了某个事实,而非修改代码。变更说明为"完善了验证"。
为何有害:流程开始保护实现,而非用户契约。这与第一卷第 20 篇中"错误后的事实弱化"是同一错误,只是规模更大:一次拉取请求就将 silent_p0 阈值从默认值 0.05(AgentClinic 基线,参见附录 D.4)放宽到 0.10,移位了整类风险。
如何修复:
- 任何放宽检查的
validation.md修改都应作为风险契约的变更单独审查; - 在变更说明中记录原因:事件标识符、事后复盘链接、预期效果;
- 禁止在未记录到
precedents.md的情况下删除必备事实; - 如果一个季度内阈值变更超过两次,这是目标与检查脱节的信号。
没有预算的层级切换
症状:当 local-coder 失效时,所有流量自动转至 frontier-reviewer。budget_keeper(预算守护者)未配置或不阻止超额。
为何有害:昂贵的层级在数分钟内消耗掉一整天的配额,当真正的 P0/P1 出现时,已无力应对。故障转移(failover)反而成为二次事件的根源。
如何修复:
- 将切换描述为有优先级的,而非全量的(参见第 9 篇);
- 仅将
severity in [P0, P1]且age > N的任务送入frontier-reviewer; - 其余任务进入降级队列,超时后转入手动通道;
- 紧急模式根据
token_health触发,并将系统切换到受保护模式,直至local-coder恢复。
无复审期限的影子规范
> 第一卷反模式的升级。 基础版本是第一卷第 20 篇中的"QWEN.md 沦为垃圾场"。在学习周期中,问题是上下文不断膨胀;在应用周期中,问题是无作者的启发式获得了契约的效力。
症状:QWEN.md 包含一个样本提示(few-shot)或启发式,不知怎么就被加进来了。作者不记得是谁、什么时候添加的。条目没有 ttl、证据或评估链接。
为何有害:启发式在没有复审程序的情况下获得了契约的效力。它无法被质疑(没人记得作者),也无法被精确验证(没有来源)。半年后,规则被类比地应用于原本未曾设想的情况。
如何修复:
QWEN.md中的任何启发式都应附上最小化的元数据头:作者、日期、证据、ttl、拍卖链接(参见第 6 篇);ttl到期后,要么更新,要么送入隔离区并记录原因;- 在过去 50 起事件中未起作用的样本提示视为删除候选;
- 影子规范不替代已批准的需求——它仅在歧义情况下引导 prompt。
没有最低人工检查的自动修复
症状:代理根据指标自动关闭事件。manual_review_floor 未设置或为零。
为何有害:即使指标看起来很干净,代理也会逐渐将人挤出回路。当出现模型未见过的某类事件时,没有备用机制来察觉偏差。几周后,安静的故障会积累起来,因为无人捕获。
如何修复:
- 明确设置
manual_review_floor:例如,"无论指标如何,至少 15% 的事件需经过人工"; - 轮换应随机选取,而非按"把最复杂的留给人"的原则——否则人工审查看不到基线水平;
- 人工审查的结果进入下一轮验证器运行的重放数据集;
- 任何
manual_review_floor的降低都作为风险契约的变更处理,而非作为优化处理。
25/25 的就绪度作为目标而非描述
症状:团队将就绪度模型的所有 25 个条目都拉到绿色,因为"必须发布"。其中部分条目是预先标绿的,没有真实的证据标记。
为何有害:就绪度作为早期信号失去了意义。下一次发布时又是"25/25",但事件又回来了。该量表变成了一种仪式。
如何修复:
- 通过
evidence_ref检查就绪度,而非通过文本声明; - 23/25 配以真实证据即准予准入;25/25 但其中两项无证据则不予准入;
- 在条目未达成时,明确记录风险和回滚条件,而非"涂成绿色";
- 定期以反向方式运行就绪度:哪些条目触发并捕获了真实问题,哪些条目在一个季度内从未触发——后者是复审候选。
缺乏更新的谱系
症状:章程的 genealogy.md 或 change_log 存在,但最后一条记录的日期是三个月前。其间规则已经变更了五次。
为何有害:出处不再作为证据链运作。半年后将无法还原"代理为何有权执行该操作",事件后的争论就会退化为泛泛而谈。
如何修复:
change_log中的记录是章程每次修订的强制部分,没有它,网关不通过 merge;parent_version必填;版本号缺失将触发单独审查;decision_hash由决策内容自动计算,以防静默替换;- 每月进行一次简短审计:将
change_log与文件的实际修改比对。不一致被记录为流程事件。
无证据标记的痕迹
> 第一卷反模式的升级。 基础版本是第一卷第 20 篇中的"口头事实"。在学习周期中,因为特性很小,所以不需要痕迹。在应用周期中,没有 evidence_ref 就无法还原某个动作是基于什么依据执行的。
症状:代理保存了动作日志,但记录中没有对源制品的引用:未指明使用的是哪一版规范、章程的哪些规则、哪一份 prompt。事件后无法还原决策的上下文。
为何有害:audit_trace_coverage(审计轨迹覆盖率)形式上接近 100%,但痕迹毫无用处。这与"无人运行 validation.md"是同一种错误,只是发生在审计层面。
如何修复:
- 每条痕迹记录都必须包含
spec_version、constitution_version、prompt_hash、decision_source、evidence_ref; - Spec CI 检查字段的完整性,如有任何字段为空则阻塞 merge;
evidence_ref是制品内的路径和标识符(如logs/node-2026-05-12.parquet#row_4123),而非对目录的一般性引用;- 任何
evidence_ref=null的记录在审计中视为无效。
诊断检查清单
如果应用 SDD 回路变得嘈杂或无法捕获回归,请回答:
constitution.md是否在执行前作为网关触发,而不仅仅在事后作为审查?mutable_rules中是否存在没有ttl或ttl超过 90 天的规则?- 在有毒规范失败后,是否至少有一个制品(
requirements.md、plan.md、validation.md)被修改? - 验证者是否使用反例、hook 日志或 JSON Schema——还是仅使用自由文本?
governance_protocol中是否有 Safety 的 veto 和确定性的 tie-breaker?- 仓库中可运行的命令是否与
[project script]接口分开标记? - 每个目标指标是否都配有 anti-Goodhart 指标?
- CI 失败时,是在修复代码还是在放宽
validation.md? - 层级切换是否有预算上限和紧急模式?
QWEN.md中的每条记录是否都有作者、证据和ttl?manual_review_floor是否独立于 KPI 的值而被保留?- 每条痕迹记录中的
evidence_ref是否都已填写?
如果有三个或以上的问题回答为否,请勿新增自动化层:文件仲裁、层级路由、新的紧急模式规则。首先消除噪声并修补当前回路中的漏洞。
总结
应用周期的反模式单独看并非灾难。危险的是它们的累积:经过几次发布后,团队在"绿色 CI"背后看不到风险契约。诊断地图是修复的第一步。每个否定回答都会转化为项目技能、Spec CI 网关,或带有可验证 rollback_condition 的章程规则。在每次重大事件后,请回到本章:同一制品在三个月后会显示出另外三个阻塞项。
第一卷的相关篇章
- 第一卷第 20 篇——SDD 基本反模式:代码之后的规范、巨型
requirements.md、仪式化的/clear、QWEN.md沦为垃圾场。 - 第一卷第 18 篇——同时也是安全威胁的反模式。
- 第 2 篇——将有毒规范作为对抗本章大多数反模式的训练工具。
- 第 10 篇——将 anti-Goodhart 作为对裸 KPI 的防护。
制品与就绪标准
| 制品 | 就绪条件 |
|---|---|
antipattern-audit.md(或回顾) | 三个字段已填写:blocker、owner、next_check;每个发现的反模式都有所有者和下一步可验证动作 |
| 检查清单 12 个问题的答案 | 已针对所选的一个制品完成;每个否定回答都对应一个计划 |
| 阻塞项与改进项的分离 | 审计不会在没有单独诊断记录的情况下,在同一变更中修复问题 |
完整轨道将更新的诊断检查清单加入 [appendix-c-checklists.md](appendix-c-checklists.md),将每个遇到过的反模式记录到 precedents.md,对反复出现的反模式补充到 QWEN.md,并至少对其中一个进行 Spec CI 检查。判定其就绪的条件是:Spec CI 中至少存在一项能自动捕获反模式的检查(例如无 ttl 的 mutable_rules),而反复出现的反模式仅在附带证据和复审期限时才会被记入 precedents.md 或 QWEN.md。
实践
- 打开团队当前的
constitution.md,检查mutable_rules中是否存在ttl和rollback_condition。至少找出一条需要更新或送入隔离区的规则。*期望:antipattern-audit.md中出现针对该具体规则的blocker / owner / next_check一行;ttl以天为单位,而非年。* - 取最近一个修改
validation.md的拉取请求。判断修改的是阈值还是事实内容。如果是阈值,检查变更说明中是否包含事后复盘链接或事件标识符。*期望:该拉取请求被记录为两种结果之一——要么是附带事件链接的、有依据的风险契约变更,要么是"红色 CI 后validation.md漂移"反模式,并附有所有者和回滚期限。* - 在所选章节中遍历
[project script]块列表,并核对哪些命令是真实的、哪些只是接口。在该章的 README 中补充相应标记。*期望:每个[project script]都明确标注"examples/...中存在可运行类比"或"自行实现";没有未标记的块。*
检验问题
- 为什么带
ttl: ∞的可变规则比根本没有表述的规则更危险? ask_storm与善意的澄清问题有何区别,如何辨别二者?- 哪三个字段使痕迹记录可用于审计,为什么在缺少它们的情况下
audit_trace_coverage=100%是一种古德哈特指标? - 在审查拉取请求时,你看到作者将
silent_p0阈值从 0.05 改为 0.10,并添加了"临时性调整,待稳定"的注释。你将如何处理该拉取请求?在合并此类变更前,必须满足哪三个条件?