规范:SSOT 分层与改动纪律
三层八文件,四道门槛,一本只追加的流水账
6小时42分钟前
aardpro aardpro
一般 SDD 与 GSDD 的分界在规范写出来之后:规范本身治不治理。本篇把 SSOT 落成一个目录——每份文件谁把控、怎么改、改了怎么记录。

上一篇立了主张,也立了分界:程序员盯 SSOT(Single Source of Truth,唯一事实来源),不盯代码;harness(护栏)管 AI 越不越界,GSDD(Governed Spec-Driven Development,治理规范驱动开发)管 harness 不管的三件事,人干什么、事实烂没烂、凭什么信。但主张悬在半空时只是口号:SSOT 到底长什么样?谁维护、谁能改、改了怎么记录?本篇把它落成一个目录。

一般 SDD(spec-driven development,规范驱动开发)与 GSDD 的分界,不在“要不要先写规范”,这一步两家完全一致;分界在规范写出来之后:规范本身治不治理。治不治理不是抽象态度,它落成两条硬边界:其一,SSOT 文件不允许任意修改(第四节);其二,每一次需求修改必须被记录(第五节)。本篇是四支柱中第二根“分层规范”的完整展开,内容是三层 SSOT、改动门槛梯度加变更流水账。读完后你应当能照着建出自己的 ssot/ 目录,并且说清每一层谁来改、怎么改、改了怎么记录。

建规范从归位开始。第一个问题甚至不是“文件怎么分层”,而是:每份文件,谁把控。

一、文件归位:每份文件谁把控

动手分层之前,先把整个代码库按“谁把控”分成三摊:

类别 住哪 装什么 谁把控
规范文件 ssot/ 什么是对的:原则、需求、口径、设计、剧本、验收 程序员(维护+盯)
护栏配置 CLAUDE.md 权限与隔离层 通过 harness 设置,明确 SSOT 修改权限归人管 人设置,机器执行(04 篇)
实现代码 src/、tests/ 等 AI 从 SSOT 派生的一切产物 AI

这张表是全篇的总依据:后面每一份文件的层、门槛、记录方式,都由“谁把控它”推出来。归位的操作只需要两句话,任何文件都逃不掉:

  1. **这份文件是干什么的?**定义“什么是对的”,执行“不许越界”,还是从规范派生出来的产物?
  2. **谁说了算?**程序员维护并盯住,人设置、机器执行,还是 AI?

拿几个常见的文件练一遍。src/ 下的任何实现文件:AI 把控。tests/ 下的测试代码:同属 AI 的派生物,它不是标准;它对验收标准的引用纪律,03 篇展开。护栏配置,从 CLAUDE.md 的权限规则到 SSOT 的隔离层:人设置、机器执行。ssot/ 下的每一份文件:程序员维护。再对一眼规范文件“装什么”那一栏:原则、需求、口径、设计、剧本、验收,六样东西恰好对应下一节的 README 头部与三层文件,一个不多、一个不少。

有一个归属值得停下来看一眼:tests/ 归 AI 把控,测试代码是 AI 写的、AI 改的,它不是标准;标准住在 acceptance(验收标准)里。

归位的用意,是把“什么归人管”从品味问题变成位置问题。ssot/ 里的东西,人必须把控,把控不了,规范就名存实亡;代码库其余的东西,人主动不盯,盯了,委托 AI 的速度优势就没了。归位一旦出错,两种病各得一种:把 AI 的派生物捡进 ssot/,人从此开始盯自己不该盯的东西;把该由人把控的规范丢给 AI 维护,“什么是对的”就跟着实现一起漂。

护栏配置是唯一由人和机器共管的部分:规则由人写,执行靠机器。它怎么落成,是 04 篇的内容;本篇只补一条与它相关的规则,护栏配置文件同样在变更记录的波及范围内(第五节),护栏规则的改动同样要留痕。

二、三层与 8 文件布局

SSOT 内部再分三层,需求层(L0)、事实层(L1)、流程层(L2),外加一个入口文件,整个目录 8 个文件:

ssot/
├── README.md              # 入口文件:头部=治理规则(见下方要点);正文=文件清单
├── L0-requirements/
│   ├── product.md         # 产品定位(稳定头部:用户、价值、范围边界)
│   ├── requirements.md    # 当前有效需求
│   └── change-log.md      # 需求变更流水账(只追加)
├── L1-facts/
│   ├── glossary.md        # 词法表:术语唯一口径
│   └── design.md          # 技术设计:架构、选型、数据库/存储格式定义、关键配置
└── L2-process/
    ├── testcase.md        # 业务流程检验剧本:多步骤走查,流程即用例
    └── acceptance.md      # 验收标准:验收项+行为矩阵+证据要求

先分清两把尺子:第一节按“谁把控”切,切的是整个代码库;本节的三层按“回答什么问题”切,切的是 SSOT 内部的纵深。三层各回答一类问题:L0 回答“要什么”,L1 固定“有哪些事实与口径”,L2 规定“怎么走查、怎么验收”。层号即地位,需求层排第一,因为所有下游文件最终都由需求派生。逐个文件说清楚。

**README.md,入口文件。**头部是治理规则,正文是文件清单。头部自上而下五块:

  1. **世界观句。**第一行就是“程序员盯 SSOT,不盯代码”。入口文件开口第一句话,就是这套规范的全部立场。
  2. **原则。**两条:唯一出处(同一事实/标准只在一处定义,其余地方只引用)与先改规范再改实现(正确性问题的修复顺序)。
  3. **角色。**负责人、程序员、AI 各自的职权。
  4. **改动门槛与流程。**各层门槛(第四节)与变更流程(第五节),含 README 头部自身的修改门槛(负责人批准)。
  5. **AI 只读。**开发 session 中对 SSOT 全层只读、严禁修改。

骨架可以直接抄(正文文件清单按项目填写):

# 程序员盯 SSOT,不盯代码

## 原则
1. 唯一出处:同一事实/标准只在一处定义,其余地方只引用。
2. 先改规范再改实现:正确性问题的修复顺序。

## 角色
- 负责人:批准 README 头部的修改。
- 程序员:SSOT 的维护者;各层改动按各自门槛执行。
- AI:负责实现;开发 session 中对本目录全层只读,严禁修改。

## 改动门槛与流程
- README 头部:须负责人批准。
- L0 需求层:变更必录,先追加 change-log.md,再更新波及文件。
- L1 事实层:修改落地前须过第二双眼睛。
- L2 流程层:程序员日常维护。
- 变更流程:需求变更 → 录入流水账 → 更新波及文件 → 实现跟进。

## AI 只读
- AI 在开发 session 中对 ssot/ 全层只读、严禁修改;SSOT 由程序员维护。

---

## 文件清单
(README 正文:8 个文件各自装什么、去哪找唯一出处)

命名纪律只有一条:文件名一律英文小写,中文标题写在文件第一行,design.md 的第一行是“# 技术设计”,打开文件先看见“这是什么”。唯一的例外是 README:它的第一行不是标题,是世界观句。这个不对称是刻意的,全目录每个文件的第一行都在回答“你是什么”,只有入口文件的第一行在回答“这里信什么”。

**L0 需求层,“要什么”。**三个文件,门槛是变更必录。product.md,产品定位:用户是谁、价值是什么、范围边界在哪。它是全目录变化最慢的文件,回答“这个产品做什么、不做什么”,范围边界尤其要紧:不做什么,和做什么一样是需求。requirements.md,当前有效需求:此刻生效的全部需求清单。留意“当前有效”四个字,它只保存现行版本,历史轨迹不在这里,在 change-log;所以动它之前必须先追加 change-log 条目(第五节的流程),让文件与账本永远对得上。change-log.md,需求变更流水账,只追加,第五节专门讲。三个文件(包括极少变动的 product.md)的任何改动都算需求变更,一律先落账。

**L1 事实层,“事实与口径”。**两个文件,门槛是第二双眼睛。glossary.md,词法表:每个术语的唯一口径,第六节展开。design.md,技术设计:架构、选型、数据库/存储格式定义、关键配置。schema 的定义就住在这个文件:审它是审白纸黑字的定义,不是逐行读实现。审设计不等于看代码,绝对主张在这里不留后门。

**L2 流程层,“怎么验”。**两个文件,门槛是程序员日常维护。testcase.md,业务流程检验剧本:多步骤走查,流程即用例;AI 开发前读它理解业务。acceptance.md,验收标准:验收项、行为矩阵、证据要求,程序员手写、验收项带编号。这两个文件与代码库 tests/ 构成三层单向引用(标准、剧本、实现,只能自上而下引用),那是 03 篇的主题,本篇不展开。

三、够用是铁律

布局到此完整:8 个文件,不增不减。这一节钉死布局背后的一条设计价值观,够用是铁律,并先说清它的准确位置:它是布局设计时的价值观,约束的是“设计 SSOT”这个动作,不是日常开发的执行;所以它留在本篇正文作设计指导,不进 README 头部的原则栏。原则栏里立的是会被反复执行的规则(唯一出处、先改规范再改实现),“够用”是对定规矩的人的自我约束。

为什么“够”字当得起“铁律”?因为 SSOT 的头号死因就是膨胀。每加一个文件,就多一个事实可能居住的地址;文件越多,“唯一出处”越难维持,去查某个口径的唯一出处,答案从“一定是那一个文件”退化成“大概率是其中某一个”。膨胀的路径总是相似的:今天加一份“路线图”,明天加一份“会议纪要”,后天加一份“评审记录”,每一份在当时都有理由,加到第十份,SSOT 自己变成了当年那个事实散落的代码库。文件越加越多、口径越写越散,SSOT 就这样死于自己要治的病。

所以 8 是封闭数字。想加第 9 个文件时,先过一遍第一节的两句话:它装的东西,是不是“什么是对的”?不是(路线图、会议纪要、评审记录多半不是),它不配住进 ssot/,放别处去。是,那它多半已有出处,只引用、不加文件。真正需要新出处的情况极少,而每一次都应当像定一条新规矩那样郑重,而不是像建一份新文档那样随手。

另一面同样重要:不引入外部框架。市面上不缺给规范文档准备的庞大分类学,要素越数越多、模型越画越全;那些框架回答的是“知识怎么归档”,SSOT 要回答的是“事实有没有唯一出处、改动有没有门槛”,前者越丰富越好,后者越简单越可靠。这 8 个文件已经是最小完备集:少一份,就有一类事实没有出处;多一份,出处就开始稀释。够用,是这套规范能被长期执行的前提。

四、改动门槛梯度:谁来改、怎么改

现在划第一道与一般 SDD 的分界线。一般 SDD 把规范写出来,任务就完成了:文件躺在仓库里,谁想改随手改;改没改、为什么改、谁改的,通常没有答案。GSDD 的第一条治理规则就落在这一点上:不允许任意修改 SSOT 文件,修改规范本身,是被治理的行为。

治理规则写在哪?README 头部(第二节的骨架)。这个位置是刻意的:规则要放在所有人必经的门口,任何新成员、任何 AI 开发 session,进 ssot/ 第一眼看到的就是它,不存在“没读到过规则”这回事。

门槛不是一个,是一个梯度,郑重程度随“变更频率 × 爆炸半径”递增:

门槛 管什么 怎么改
负责人批准 README 头部 向负责人提出改动,批准后才落盘
变更必录 L0 需求层 先追加 change-log 条目,再动文件(流程见第五节)
第二双眼睛 L1 事实层 落地前过一双与动笔者不同的眼睛
日常维护 L2 流程层 程序员随改随更,无需仪式

SSOT 三层八文件与四道改动门槛:README 头部须负责人批准,L0 变更必录,L1 过第二双眼睛,L2 日常维护

逐级说清“为什么是这个门槛”。

**头部,负责人批准。**头部是容器之壁:原则、角色、门槛任何一条被改动,整个 SSOT 的治理语义跟着变,它改的是“谁能改什么”本身。需求是内容之巅,头部是容器之壁;墙改得最难,不是墙比内容高贵,而是墙一塌,里面全部内容失去保护。所以头部是全目录唯一需要“批准”的地方:不改则已,改必过负责人。

**L0,变更必录。**需求是内容之巅:每个需求都被下游引用,设计按它做、剧本沿它走、验收拿它判,需求一改,波及面天然最大。这一层的门槛不是“不许改”(需求当然要能改,不许改的规范是假规范),而是“不许白改”:每一次变更先落账,让“需求在什么时候变成了什么”永远答得上来。账目的格式与全流程,第五节展开。

**L1,第二双眼睛。**事实层的错误有自己的脾气:不报错、不崩溃、静默扩散。design.md 里一个字段口径错了,实现代码会把错误抄到每一处用到它的地方,AI 抄起来尤其快;错误在源头只值一行字,扩散之后再修就是一场工程。所以这一层的修改在落地前必须过第二双眼睛,把关的眼睛与动笔的眼睛不能是同一双。AI 起草的修改稿同样适用:AI 可以起草,草稿要经这双独立的眼睛逐条复核后才进 SSOT,起草与把关分离,采纳了才算数。单人项目没有第二双眼睛在场,门槛降级为时间与身份:改动放一夜,隔天以审稿人而非作者的身份逐条复核后再落盘。时间隔出距离,身份换出立场,两样都没有的自审等于没审。

**L2,日常维护。**剧本与验收跟着开发节奏走,是全目录改动最频繁、爆炸半径最小的一层,门槛相应最轻:程序员日常维护,随改随更。轻,不等于没人管,改动者仍然只有程序员(AI 只读,见下),验收项的编号与引用纪律是 03 篇的内容。

最后一条规则横跨全层:**AI 在开发 session 中对 SSOT 全层只读、严禁修改,SSOT 由程序员维护。**包括门槛最轻的 L2。这条是岗位本体论的机制底线:如果 AI 能在干活时顺手改规范,规范就不再独立于实现,“盯 SSOT”盯到的将是 AI 自己的输出,治理依据与被治理对象合一,治理失效。只读怎么真正守住是 04 篇的事,本篇先把它写进头部规则。顺带把冲突时改哪边说死:AI 的产出与 SSOT 冲突,改的是实现;确系 SSOT 错了,走上面的门槛由人来改。两个方向都不能反。

五、变更流程与流水账

第二道分界线在这:一般 SDD 对“规范怎么变”多半没有答案,改了就改了。GSDD 的回答是一条硬规定:**每一次需求修改必须被记录。**01 篇把病根钉在“没有人知道需求在什么时候变成了什么”,change-log.md 的存在,就是让这个问题永远答得上来。01 篇那场六周拉锯的复盘里,“变更没记录、也没同步”是多重失守之一,这条只追加的流水账,堵的就是这一失。

流水账三条铁则。其一,只追加:条目不删、不改、不重排;写错的条目也不动,追加一条新条目订正。账本的完整性高于账本的整洁,因为每一次“顺手修一下历史”,销毁的都是证据。其二,条目格式:日期、新需求描述、波及文件。其三,波及范围不限于 ssot/:护栏配置文件同样要进波及清单,比如 CLAUDE.md 里的权限规则。护栏规则同样算数:新需求引入了新的危险操作,权限跟着改,这次改动同样要留痕。

一条真实的条目长这样:

## 2026-11-03
新需求:新增“导出为 CSV”功能;导出任务允许读取用户的全部联系人数据。
波及文件:
- ssot/L0-requirements/requirements.md(新增导出需求条目)
- ssot/L1-facts/design.md(导出器选型、CSV 字段与转义约定)
- ssot/L2-process/acceptance.md(新增导出验收项)
- ssot/L2-process/testcase.md(导出流程走查步骤)
- CLAUDE.md(导出命令加入允许列表)

流程四步,顺序锁死:需求变更 → 录入流水账 → 更新波及文件 → 实现跟进。

变更流程四步:需求变更 → 录入流水账 → 更新波及文件 → 实现跟进,账先于改动、顺序锁死

  1. **需求变更发生。**来源不限:用户反馈、负责人改主意、开发者自己想通了。判据只有一条,“什么是对的”要变,就是需求变更。
  2. **录入流水账。**先追加条目,再动任何文件。顺序是刻意的,账本的全部价值都在这条顺序上:账先于改动,任何时刻翻账本,“什么时候改的、改成了什么、波及了谁”都有答案;反过来先改后补,漏记会成为常态,补记永远比动手慢半拍。
  3. **更新波及文件。**按条目里的波及清单逐个更新:L0 的 requirements、L1 的 design 与 glossary、L2 的 acceptance 与 testcase,乃至护栏配置里的 CLAUDE.md。找波及文件的判据也只有一个:这个文件里,有没有哪句话因为这次变更而不再为真?有,就进清单。漏更新的波及文件是旧口径的藏身处,SSOT 里只要剩一处旧话没改,AI 就会把它当成现行的口径抄出去。
  4. **实现跟进。**SSOT 更新完毕,AI 按新规范对齐实现。方向永远单向:实现向规范看齐,规范绝不向实现迁就。这一步干得怎么样,由验收证据说话,03 篇。

最后划清账本的边界:change-log 记的是需求变更,不是代码改动。代码是 AI 的实现产物,它每一次变动的痕迹与对错,由验收体系判定(03 篇),不需要这本账来记。账本只回答规范侧的问题:规则什么时候改的、改成了什么、波及了哪些文件。

六、词法表与设计交叉引用

唯一出处原则(README 原则第一条)在事实层有两个最容易被凿开的口子:术语与字段。

glossary.md 管术语的唯一口径。每个术语一条、一条一个定义:“客户”、“有效会话”、“活跃用户”这类词,全项目只有一份解释。术语没有唯一口径的下场不难想:每个文件各用各的,看似同一批词,实则各说各话。口径散,就是从词开始散的。

design.md 管字段,架构、选型、数据库/存储格式定义、关键配置。字段是设计的基本单位,也是最容易漂移的单位,两个方向都会漂:同一个含义,这里叫 user_id、那里叫 customer_id;同一个名字,这里指人、那里指账号。两种漂移初期都不报警,等发现两条链路给出矛盾的结果时,两份口径已经各自走了很远。

把口子焊住的规则只有一句:design.md 的每个字段必须能映射到 glossary.md 的术语。

映射长什么样,两份文件各看一行:

# glossary.md
- 客户:与企业发生或可能发生业务关系的自然人或组织。

# design.md
- `customer_id`(BIGINT):客户唯一标识,客户表主键。

对起账来一眼见分晓:customer_id 落回“客户”,含义一致,通过。词的定义住词法表,字段的类型与约束住设计,各管一半,谁也不复制谁。

这是一条可执行的检查,不是口号。写 design、审 design 时逐字段问一句:这个字段对应词法表里的哪个术语?答案只有三种,各有处置:

  1. 映射得上,含义一致,通过,下一个字段。
  2. 词法表里没有这个词,说明设计引入了新概念:先把它立进词法表(L1 的修改,过第二双眼睛),再回来写这个字段。
  3. 词法表里有,但含义对不上:要么字段起了个别名(改回唯一术语),要么口径本身要变(先走词法表的修改门槛,再同步字段)。

三种处置指向同一件事:**同一件事只在一处定义。**术语的口径住在词法表,别处只引用;字段只是术语在设计里的落点,不是第二处定义。注意这条规则是单向的:design 的字段必须能映射到词法表,但不要求词法表的每个术语都被 design 使用,术语还服务于需求与验收的表述。

这个检查的成本,是每次审设计时逐字段一句话;挡住的,是漂移的种子。设计评审千头万绪,这是一道五分钟能跑完、每次都能跑出确定性答案的检查,它让“口径唯一”从一种愿望变成一道工序。

本篇到此收拢。照第二节的目录建出 ssot/,README 头部立起治理规则,四道门槛各就各位,change-log 开始记账,“什么是对的”有了住处、有了主人、有了修改的规矩,每一层谁来改、怎么改、改了怎么记录,都有了答案。但规范只回答了上半场;下半场的问题是“凭什么信”:AI 交回的结果,凭什么判定它符合规范?验收与测试怎么重构,是 03 篇的事。

下一篇,执行。

统计
10
© aard.ltd 2024-2025 ✉ 239479@qq.com