模式之前的手工纪律
Sentinel 在一个已经一个月没有刷新的 plan 上连续跑了七个 Charter,靠手工执行 CHARTER-18,心里清楚任何一步走错都会埋掉前六个 Charter 的工作。五个小时之内,把这套手工纪律写进了上游治理 —— 当时还没给这个模式起名字。
1. 正确的问题
Issue #150 于 5 月 14 日 16:59 UTC 开启,寻求的不是一个是/否的答案。操作员要求的是重新表述这个问题:
"bridge 文档真正需要回答的是如何做,而不是是否做:在多 Charter 执行期间,什么纪律能确保 spec 与代码保持同步,同时避免刷新步骤对已经交付的部分撒谎。"
这套纪律已经存在了。就在那时,它正在 Sentinel 中被手工应用于 CHARTER-18,操作员心里清楚任何一步走错都会埋掉前六个 Charter 的工作。缺少的——Issue 以令人不安的精确度所记录的——是名字。是时机。是未来的采用者无需经历自己摸索之痛就能遵循的规则。
这篇文章涵盖从那个 Issue 到将纪律规范化为上游治理的 PR 之间的五个小时、为规范化提供依据的 12 条实证经验,以及在模式存在之前就演练了该模式的具体操作事件——Sentinel 的 CHARTER-18。下一篇文章将介绍 27 小时后发生的事:名字。
2. 在同一个 plan 上跑七个 Charter
一句话背景:Sentinel 执行 specs/002-commshub/plan.md——写于 2026 年 4 月 21 日 commit be29421——历经连续七个 Charter(CHARTER-07 至 CHARTER-17),整整一个日历月,一次都没有刷新 plan。
原始 plan 写得很好。它经过精心撰写,详细描述了四个用户故事(US1-US4),声明了数据模型,勾勒了契约。但这个 plan 是操作员在四月时对代码行为的一个快照式设想。执行过程中发现了 plan 无法预见的事情:
- 执行过程中出现的基础设施模式,并被证明是可复用的(
core.processed_events中的命名空间、withRLS辅助函数、用于不变量执行的 PL/pgSQL 触发器)。 - plan 假设已解决的代码缺口(
AnomalyDetector半成品、缺失幂等性去重、Recipient.TenantIDschema 缺口)。 - 在执行过程中逐渐固化的 Charter/审计模式(跨家族审计周期、HTTP 层测试覆盖缺口、带严格大于的游标分页)。
当需要为 US5 规划 CHARTER-18 时,plan 已经一个月没更新,而代码上面积累了一个月的实证经验。两者不同步。而 SPECKIT-CHARTER-BRIDGE.md——通常应该回答*"这里该怎么做?"的框架文档——对这种情况保持沉默。规则"在多 Charter 执行期间如何让 spec 与代码保持同步"*还不存在。
3. 十二条实证经验
Issue #150 列举了在 plan 中未经反思而积累的十二条经验。这份按类型分组的原始表格值得保留在视野中:
| # | 类型 | 经验 |
|---|---|---|
| 1 | 基础设施模式 | 使用命名空间 module='commshub' 的 core.processed_events 可复用——plan 描述的是自己的表。 |
| 2 | 基础设施模式 | EnvSecretLoader + Secret Manager 映射(PRs #70/71)——plan 描述的是直接访问。 |
| 3 | 基础设施模式 | withRLS 辅助函数作为租户隔离的唯一真实来源——plan 没有锚定它。 |
| 4 | 基础设施模式 | FR-005 PL/pgSQL 触发器作为不变量执行——plan 描述的是应用层检查。 |
| 5 | 代码缺口 | AnomalyDetector 半成品——plan 暗示它是可用的。 |
| 6 | 代码缺口 | delivery_log 层的幂等性去重缺失——plan 暗示已协调好。 |
| 7 | 代码缺口 | Recipient.TenantID schema 缺口——plan 引用 tenant_id 时假设它已存在。 |
| 8 | 代码缺口 | 每层速率限制覆盖缺失——plan 说*"3 层层次结构"*但没有提到 US1 实现的是统一层。 |
| 9 | Charter 模式 | 跨家族外部审计周期强制(连续 6 次)——plan 未提及。 |
| 10 | Charter 模式 | HTTP 层测试覆盖缺口——plan 对测试分层保持沉默。 |
| 11 | Charter 模式 | 带严格大于的游标分页元组——plan 保持沉默。 |
| 12 | Charter 模式 | Dispatcher 接口稳定交换模式——plan 保持沉默。 |
执行期间发现或完善的四个基础设施模式。plan 未预见到的四个代码缺口。当场固化的四个 Charter/审计模式。十二条,在一个月内,全部真实。plan 没有它们。而下一个 Charter——CHARTER-18——将要读取 plan,仿佛它们不存在。
操作员在 Issue 中记录的估算是诚实的:
"由过时前提继承导致 ≥1 个关键/高危发现的估计概率:~50%。"
一句话,就是掷硬币。一半在过时 plan 上启动的 Charter 会以一个后续审计周期必须在关闭前原子性修复的关键发现结束。另一半只是侥幸逃脱。
4. 方案四:手工应用的纪律
同一天,在这套纪律还没有名字之前,Sentinel 内部发生的事:操作员评估了五个具体方案,选择了第四个。Sentinel 的私有 AIDEC——AIDEC-2026-05-14-001-speckit-plan-scope-limited-us5-refresh.md——毫不掩饰地记录了这一分析。我在这里转述它,因为详细内容在私有仓库中,但推理结构才是关键所在:
- 方案一——"和以前一样读"。 保持 plan 过时状态,像前六次一样读取。代价:在后续审计周期中偿还债务,约 50% 的关键发现概率。
- 方案二——用
/speckit-plan重新生成一切。 风险:覆盖关于 US1-US4 的断言,而真实代码并不满足这些断言;生成器不知道当前状态。 - 方案三——不受限制地在另一个 PR 中刷新。 与方案二相同的风险,加上评审摩擦。
- 方案四——范围限定刷新 + 3 个关卡。 选定方案。
- 方案五——推迟 CHARTER-18,先做重构 Charter。 日历成本高,相对方案四边际收益低。
第四个方案由三个具体机制组成:
第一机制——范围限定提示词。 带着明确限制性提示词运行 /speckit-plan:仅重新生成对应 US5 的第 7+8 阶段;将 US1-US4 逐字节保留原样,用智能体必须遵守的 <!-- LOCKED: prior US shipped --> 注释标记。没有这个明确限制,SpecKit 的重新生成是破坏性的:它重写整个 plan,覆盖真实代码已经与之矛盾的描述。
第二机制——关卡 (a):对代码现实的验证。 一个临时脚本,约 30 行 bash + grep,将 data-model.md 中每个非 US5 实体与 db/migrations/*.sql 中的真实 schema 进行差异比较,并将 contracts/*.md 中每个非 US5 端点与 internal/modules/commshub/handler_*.go 中的实际处理程序签名进行比较。任何分歧都会阻止合并。脚本存在于 Sentinel 的仓库中;它不优雅,它是操作性的。框架后来规范化为关卡 (a) 的东西当时已经在那里了,以手工 bash 的形式。
第三机制——关卡 (b):逐 hunk 细粒度评审。 逐文件、逐 hunk 评审 git diff,对锁定部分的任何变更都不接受,除非有理由说明。操作员在 AIDEC 的 R1 中明确记录了预期的摩擦:如果智能体重新生成的 hunk 比必要的多,人工评审者会感到疲惫,纪律就会崩溃。缓解措施:超过 50 行的 hunk 触发 P1 评审。
第四机制——关卡 (c):两个 PR 拆分。 刷新是一个独立的 PR,针对当前代码进行评审。随后的 Charter 填充是另一个 PR,针对已经刷新的 plan 进行评审。不要混合两个评审——因为如果混合在一起,刷新的债务就会藏在新工作后面。
四个机制。手工应用,没有任何上游文档支持。一个单独的采用者(我),一个单独的会话,以及始终伴随着的感觉——在解决一个以前从未有人解决过的案例。
5. 结果
CHARTER-18 当天就关闭了。Charter 遥测指标,逐字记录在关闭 YAML 中:
estimation_drift_factor: 1.0—— Charter 恰好在估计的小时范围内完成。pre_work.items_discovered_during_planning: 0—— 规划过程中没有出现意外事项。overall_satisfaction: 5/5—— 操作员在整个链条中最好的主观校准评分。
比指标更重要的是:CHARTER-18 是七个 Charter 链条中第一个在不需要中途修复 Charter 的情况下关闭的。前六个各自至少需要一次原子性事前关闭修复,以弥补声明与交付之间的分歧。CHARTER-18 没有。操作员的结语,直接引用自后来的 CHARTER-CHAIN-EVOLUTION.md:
"PR #76 的 SpecKit 刷新消除了大部分导致先前 Charter 漂移的歧义。不需要中途修复 Charter——research.md 中的 EC1..EC15 实证修正清单将本来是执行前风险的东西吸收为执行中意识。"
手工纪律奏效了。它不只是干净地关闭了 Charter;它改变了风险的性质。过去是执行前风险(在 Charter 执行期间发现分歧,原子性修复)的东西变成了规划前意识(EC1..EC15 清单在声明 Charter 之前列举实证修正)。风险没有被消除;它被移到了一个可以管理的时刻。
6. 四小时四十分钟
时间线值得精确记录,因为这正是博客所要记录的:
- 5 月 14 日,16:59 UTC —— Issue #150 开启。十二条经验被列举,三个关卡被提出,概率分析被公开。
- 5 月 14 日,21:39 UTC —— PR #152 合并。
fw-4.14.3发布。*"多 Charter 执行期间的 spec 维护"*作为SPECKIT-CHARTER-BRIDGE.md的新章节添加,三个关卡被逐字规范化为框架治理。问题与上游答案之间相隔四小时四十分钟。
PR #152 作为治理文档记录的,正是私有 AIDEC 在几小时前手工应用的。三个关卡同名:对代码现实的验证(关卡 a)、逐 hunk 细粒度评审(关卡 b)、两个 PR 拆分(关卡 c)。另加四条何时刷新的启发式规则:同一 plan 上 ≥3 个已关闭 Charter、≥4 周 + ≥2 个 Charter、R<N>(new) 数量 > 6、目标 US 触及已完善的基础设施。另加一条明确说明为何不重新运行 /speckit-tasks 的注释(因为它会销毁 [X] 标记和构成历史追踪的 *CHARTER-NN:* <sha> 注解)。另加一条路线图注释,提到未来将机械化关卡 (a) 的 straymark spec-drift CLI。
四小时。毫不夸张地说,框架在操作员另一个窗口里还开着 git diff 的时候就写好了指导文档。纪律与其规范化几乎是同时发生的。这值得记录,因为它颠倒了学术治理机构通常假设的顺序:先理论,后实践。这里反过来了。先实践,在它还疼的时候。再理论,在疼痛还新鲜的时候。
7. 结语
我从这个过程中得到的,用四个论点:
-
模式源于纪律,而不是反过来。
fw-4.14.3的三个关卡不是抽象设计的;它们是从一个具体的操作事件中提取的,在手工应用数小时后。当框架尊重顺序时,治理是后实证的。 -
明确的概率就是纪律。 *"过时前提继承导致关键发现的概率约 50%"不是贝叶斯精确度;它是一个公开声明,迫使决策必须有理由支撑。没有这个数字,"我们应该刷新吗?"*的问题由直觉决定。有了它,就由风险算术决定。
-
三十天内十二条经验是实际的节奏。 这不是可以用*"以后再读"*捕获的噪声。这是原始 plan 无法预见的实证密度,它摧毁了任何长期规划假设。智能体辅助开发的节奏就是如此。
-
四小时不是记录;它是过程的属性。 当纪律已经在一个领域中被应用时,将其规范化到上游是执行,不是设计。正如我们在第 4 篇文章中关于外部审计周期所见("一天内五个 PR"),以及第 6 篇文章中关于品牌重塑("四十三分钟")。框架没有发明这套纪律;它提取了它,并给它起了名字。
接下来,在下一篇文章中,是这个博客所要讲述的弧线的最后一个事件:"模式 1 + 模式 2——链条演化"(H-12)。在 PR #152 之后 27 小时,三个治理关卡结晶为两个命名元模式——事前声明刷新和浮现即修正——附带遥测 schema 和 CLI 辅助工具。这是第 1 篇文章从前方打开的弧线的收尾。
锚点:Issue #150(5 月 14 日 16:59 UTC)。PR #152 —— fw-4.14.3(5 月 14 日 21:39 UTC)。规范化章节:SPECKIT-CHARTER-BRIDGE.md §"多 Charter 执行期间的 spec 维护"。CHARTER-18 指标报告于 CHARTER-CHAIN-EVOLUTION.md(fw-4.16.0)。私有来源依照 GUIA-EDITORIAL.md §7 转述:Sentinel 仓库中的 AIDEC-2026-05-14-001-speckit-plan-scope-limited-us5-refresh.md。
本文档在生成式 AI 工具(Claude 4.7)的协助下撰写;内容的全部责任由人类作者承担。