在参与 Maka PR #5308 的过程中,我经历了多轮设计调整和代码审查。这个 PR 的具体功能并不是本文重点。更值得记录的是,为什么一些局部看似合理的修改,会逐渐造成设计报告失真、模块职责重叠和测试数量膨胀。

这些问题很难靠一份事后总结解决。它们发生在实现过程的每一步,需要 Agent 在阅读需求、选择方案、处理 review 和补充测试时持续检查。基于这次复盘,我整理了两类约束:写入 AGENTS.md 的常驻 PR 规范,以及负责完整执行流程的 pr-lifecycle Skill

先区分 SPEC、RULE、设计和实现#

复杂 PR 容易失控,一个常见原因是把不同层次的决定混在一起。为了避免这种混乱,我将它们分为四类:

层次需要回答的问题合适的载体
开发过程规则Agent 应当怎样完成 PRAGENTS.md
SPEC系统必须满足什么完整行为契约Issue 或规格文档
架构设计哪个模块负责,通过什么接口协作设计报告
实现机制使用什么算法、状态和数据结构代码及设计报告的实现部分

RULE 是 SPEC 中一条局部成立的事实,例如某个操作必须只读、某个列表必须有界读取。它不能代替完整 SPEC。SPEC 还要交代用户场景、操作顺序、失败语义、资源限制、非目标和验收标准。缺少这些上下文,规则之间无法组成完整的行为视图。

架构设计承担下一步工作:将 SPEC 中的义务分配给具体模块。实现机制则位于更下一层。只要对外契约不变,算法和存储方案应当可以替换。把当前实现过早写成产品要求,往往会让后续调整变得困难。

因此,设计工作的顺序应当是:先确认必须保证的行为,再决定由谁负责,最后选择满足这些义务的最小机制。

设计报告是一份当前契约#

PR 开始时写过设计报告,不代表后续实现天然符合它。review 可能暴露新的事实,实验也可能证明原方案代价过高。设计允许变化,但报告必须同步变化。

我将设计报告定位为“当前实现契约”。它描述当前代码如何满足 SPEC,而不是保存 PR 最初方案的历史版本。设计发生调整时,应在同一轮修改中完成四项同步:

Issue / SPEC
当前设计报告
代码实现
验证义务的测试

最终报告不应继续保留已经删除的升级路径、临时兼容逻辑、一次性脚本或废弃机制。设计演变的原因可以记录在 review 回复或 decision log 中,当前设计与开发历史需要分开维护。

这种同步也不能反过来变成“代码先改,文档随后为代码补理由”。当实现偏离原设计时,应重新确认 SPEC 义务,说明哪项设计判断发生了变化,再更新实现和测试。

每条规则只能有一个 authority#

多模块系统中的“职责边界不清”经常被理解为代码放错目录。更准确的问题是:同一条规则被多个模块分别解释。

设计报告至少需要说明三个概念:

概念含义
Authority最终决定某条规则、状态或错误语义的模块
Interface调用方正确使用模块必须知道的约束
Seam其他模块调用或替换该能力的位置

一条业务规则、状态变化、错误分类或资源上限,应当只有一个 authority。其他模块可以调用结果、传递结果或负责展示,但不应重新推断同一结论。

这个要求直接改善了代码的 locality。规则变化时,修改和验证集中在负责它的模块,不会扩散到多个客户端。判断模块归属时,可以检查:状态由谁产生和修改,错误由谁标准化,资源由谁测量,以及删除该模块后,复杂度是消失还是重新散落到所有调用方。

复杂度扩散是设计预警#

复杂实现不一定错误,但新增复杂度必须能对应明确的 SPEC 义务。如果一个局部问题开始引入跨 package 状态、协议字段、错误码、缓存、TTL/LRU、客户端恢复逻辑和多语言文案,应暂停实现并重新检查设计。

通常需要排查两种情况:当前方案是否提供了 SPEC 没有要求的更强保证;原本属于一个模块的决策是否泄漏给了多个调用方。

这项检查越早进行越好。等到所有层都依赖新机制后再回退,删除成本会明显增加。

Review 指出问题,不替代设计判断#

Review comment 经常定位到一个具体文件或一段代码,但那个位置可能只是问题被观察到的地方。机械地在原地增加判断,容易留下其他调用路径,或者继续扩大错误的模块职责。

处理 review 时,我现在采用下面的顺序:

  1. 将现象还原为被违反的 SPEC 义务;
  2. 找到这条规则真正的 authority;
  3. 检查是否有其他调用方重复实现同一判断;
  4. 在 authority 中完成最小修复;
  5. 增加能够在旧行为上失败的回归测试;
  6. 同步更新设计报告,并删除被取代的机制。

Reviewer 的建议是定位问题的重要证据,最终方案仍需符合需求范围、现有架构和实现成本。尤其当建议隐含了更强保证时,需要先确认它是否属于 SPEC,而不是直接把它固化进协议。

测试应围绕最终义务组织#

多轮修改很容易形成一种测试结构:每修复一次,就在被指出的位置增加一条测试。最终同一规则可能在适配器、协调层和界面各测一遍,而一些测试保护的仍是已经废弃的中间机制。

测试消融用于解决这个问题。它不是追求更少的测试,而是重新检查每条测试保护的对象:

设计发生替换后,应删除旧机制对应的测试。测试数量减少并不代表覆盖退化;如果留下的测试更接近规则所有者,也更能表达业务意图,验证反而更加可靠。

为什么同时需要 AGENTS.md 和 Skill#

复盘得到的原则必须进入 Agent 的工作环境,否则下一次 PR 仍然依赖临场记忆。但所有细节都写入全局指令,也会让简单任务承担不必要的流程成本。

因此我采用了两层结构。

AGENTS.md 保存任何非简单 PR 都应遵守的硬约束:先定义契约再选择机制;每条规则只有一个 authority;SPEC、设计报告、实现和测试保持一致;review 修复回到规则所有者;设计变化后执行代码与测试消融。

pr-lifecycle 负责按需展开完整工作流:

Issue / 用户目标
→ 恢复事实
→ Grilling 形成 SPEC
→ SPEC 门禁
→ 设计报告与 authority 表
→ 按义务切片实现
→ Review 循环
→ 测试消融与最终一致性审查
→ Re-review / 合并

Skill 同时提供 SPEC/设计报告模板,以及 review 与消融检查表。它要求 Agent 产出可检查的契约、归属表和验证结果,而不是笼统承诺“注意模块边界”。项目自己的 AGENTS.mdCONTRIBUTING 和 CI 规范仍拥有更高的具体性,用来补充仓库特有要求。

这套方法的适用范围#

并非每个 PR 都需要完整流程。单行修复只需明确问题和验证方式;涉及多个模块、协议、持久化、并发或资源控制时,书面 SPEC、authority 表和设计门禁才真正有价值。

pr-lifecycle 也不能替代产品决策、维护者 review 或项目 CI。它解决的是过程中的一致性:让 Agent 在连续修改中仍能说明当前系统保证什么、由谁保证,以及哪些测试能够证明这些保证。

这套规则还会随着后续 PR 继续调整。它目前提供的基线很明确:任何新增机制,都应能追溯到一项 SPEC 义务;任何业务规则,都应能找到唯一的 authority;最终设计报告只描述当前真实实现。