docs(design): 落成边界、续跑、公共 API 形状与停止语义四份决策
第 ② 阶段需求对齐与第 ③ 阶段架构的产出,代码尚未开始。 design/0001 定边界判据:三道测试(时机 / 信息 / 性质)全过才在界内, 外加「只认接缝、不认接缝后面是什么」与不夺走下游实验因子的排除条款。 design/0002 定步级续跑:不承诺原子性,承诺绝不静默丢失与不替工具猜幂等性; 先写意图再执行、结果 ID 预分配、重放策略由工具声明且默认绝不重放。 design/0003 定公共 API 形状:单一入口两个动词、五个接缝、三个伪接缝的排除理由、 分层与九条依赖规则。design/0004 定停止判定顺序、十个停止原因取值与步记录字段表。 0003 与 0004 需过 CLAUDE.md §2 人类门,已由项目负责人确认,状态转为已接受。 explanation/scope.md 与 explanation/architecture.md 是这四份决策的常青回写, 分层与模块边界的权威在 architecture.md,将来由 import-linter 契约机器断言。 migrations/ 下 dissect 是唯一的硬迁移验收,govdoc-saas 只做设计级对齐。 三道闸都过了:14 agent 对抗辩论定骨架,两轮硕士生阅读报的 30 余条已修完, Codex 对抗审查抓出的两条致命问题(提交型完成被误判成环境故障、 崩溃恢复漏一个状态)已修,修完的形状还没送 Codex 复审。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+66
-4
@@ -57,6 +57,32 @@
|
||||
哪份该更新、哪份不该更新,结果是全都不更新。那一版的 `API.md` 落后代码 210 个提交,
|
||||
`CLAUDE.md` 落后 676 个提交,而两份文档当时都写着要保持同步。
|
||||
|
||||
### 两层的写法不一样,写成一样就说明有一层写错了
|
||||
|
||||
同一件事在两层各出现一次是正常的:分层是什么、接缝有哪些,`design/` 里定过一遍,
|
||||
`explanation/` 里还要描述一遍。**重复的是事实,不重复的是理由。**
|
||||
|
||||
| | `design/` | `explanation/` |
|
||||
|---|---|---|
|
||||
| 回答什么 | 当初为什么这么定 | 现在到底是什么样 |
|
||||
| 时间 | 有时间性:当时的处境、比较过哪几个、否决了什么 | 无时间性,只描述现在 |
|
||||
| 主语 | 我们、这次决策 | 系统里的东西:这一层、这个接缝、这条规矩 |
|
||||
| 备选方案 | 必须写全,含否决理由 | 不写——备选方案是历史 |
|
||||
| 理由 | 完整论证 | 就地一两句,展开指回 `design/` |
|
||||
| 冲突时 | 冻结,可能已经过时 | **以它为准** |
|
||||
|
||||
**记录层写成法条就走音了。** ADR 实践里这个反模式有个名字叫「Blueprint or Policy in
|
||||
Disguise」——本该是一份记录活动及其结果的日志,写着写着变成了菜谱或者法条那种命令式、
|
||||
权威式的口吻。design doc 该像一份设计讨论的笔记:叙述、有处境、有取舍。
|
||||
|
||||
**常青层写成自述也走音了。** 主语一旦变成「这份文档」「这一节」「这里」,动词一旦变成
|
||||
写作动作(不复述、列出来、说清楚、免得读者),语气就错了,**哪怕那句话本身有道理**。
|
||||
改法是把主语换回系统。判据在语气,不在内容——那种句子往往真的带着信息,按「删掉之后信息
|
||||
有没有少」来判会把它留下来,而它照样读着别扭。
|
||||
|
||||
**常青文档里的决策索引只索引、不复述理由。** 复述会漂移:旧文档冻结着,索引里那句转述
|
||||
却跟着人的记忆变,几个月后两处就对不上,而机器查不出来。
|
||||
|
||||
## 2. 常青层:四类,各自要有更新触发点
|
||||
|
||||
前三类的划分借自 Google 的工程文档实践,以及 Diátaxis——一个把文档按「读者此刻想干什么」
|
||||
@@ -201,10 +227,46 @@ design doc 冻结的时候,它的结论必须已经落到两处之一:
|
||||
形状已经定了、代码还不存在」本身就是一种真实情况——写清楚形状,再写明它还没有代码、缺口在哪,
|
||||
就够了。
|
||||
|
||||
**冻结与取代**:写完不改。决策变了就新写一份,在新文档开头标 `supersedes: 0001`,旧的原样留着。
|
||||
这是从 ADR(Architecture Decision Record,架构决策记录,一种把每次架构决策单独存成一份不可修改
|
||||
文件的做法)里保留下来的唯一一条机制。成本很低,但记录层的价值全靠它——只有旧文档还在,
|
||||
你才能看出决策是怎么演变的。
|
||||
**冻结**:写完不改。决策变了就新写一份,旧的原样留着。这是从 ADR(Architecture Decision
|
||||
Record,架构决策记录,一种把每次架构决策单独存成一份不可修改文件的做法)里保留下来的
|
||||
唯一一条机制。成本很低,但记录层的价值全靠它——只有旧文档还在,你才能看出决策是怎么演变的。
|
||||
|
||||
### 新文档和旧文档之间是什么关系,在头部写清楚
|
||||
|
||||
一份新的 design doc 很少是凭空长出来的,它多半跟已有的某几份有关系。**只有「整份作废」
|
||||
一种关系是不够用的**——大多数时候动的只是旧文档里的一小块,而那份文档的其余部分还在生效。
|
||||
所以关系词有五个,每个都写成 `**关系词** 目标 + 具体到哪一节`:
|
||||
|
||||
| 关系词 | 什么时候用 | 必须同时写清楚 |
|
||||
|---|---|---|
|
||||
| **取代** | 推翻旧文档的某个结论 | 取代的是哪一节;那份文档的其余部分是不是仍然有效 |
|
||||
| **补充** | 沿着旧文档的某条决策继续往下定 | 补充的是哪几条决策 |
|
||||
| **回答** | 填掉旧文档明写「还没定」的坑 | 回答的是哪一节留的坑 |
|
||||
| **触及** | 本文的结论要回写进哪份常青文档 | 回写到哪一节;**不回写这份 design doc 就是死的** |
|
||||
| **不取代任何文件** | 填的是一块从来没人填过的空白 | 为什么这块空白到现在才填 |
|
||||
|
||||
「取代」必须点名其余部分仍然有效,否则读者会以为整份旧文档作废了,连那些还在生效的决策
|
||||
一起丢掉。
|
||||
|
||||
**旧文档不回标。** 被取代的那份不加任何指向新文档的痕迹——加了就是改内容,而记录层的价值
|
||||
全在于它没被改过。读者靠常青文档里的**决策索引**找到当前有效的那份,而不是靠在 `design/`
|
||||
目录里翻。所以决策索引是常青文档的必备一节,它只索引不复述理由。
|
||||
|
||||
代价说清楚:直接跳进某一份旧 design doc 的人,有可能读到一个已经被取代的结论,而那份文档
|
||||
里没有任何东西提示他。**这是接受了的风险**,换来的是记录层真的不可变。降低风险的办法是让
|
||||
常青文档成为入口——任何一个问题,先在常青层找到答案,再顺着决策索引跳进 design。
|
||||
|
||||
### 状态字段
|
||||
|
||||
头部写 `**日期** YYYY-MM-DD · **状态** X`。状态只有两个取值。
|
||||
|
||||
**待确认**——这份 design doc 定的东西按 `../CLAUDE.md` §2 要过人类门。**这种文档必须在正文
|
||||
最前面写明:在它被确认之前,什么不许做。** 只写「待确认」是不够的,那让读者无从判断这份
|
||||
文档算不算数;写清楚被阻塞的是哪件具体的事,这个状态才有操作意义。
|
||||
|
||||
**已接受**——确认之后改成 `已接受(YYYY-MM-DD 项目负责人确认)`,并在正文留一句说明它
|
||||
原来是待确认、为什么要过门。**改状态字段不算破坏冻结规则**,因为决策内容一个字没动。
|
||||
不需要过门的 design doc 写完直接就是「已接受」。
|
||||
|
||||
**和 `explanation/architecture.md` 的分工**(这两份最容易搞混):改一次架构,两份都要动,但写的
|
||||
东西不同。design doc 写「我们当时面对什么问题、比较了哪几个方案、为什么选了这个、放弃了什么」,
|
||||
|
||||
Reference in New Issue
Block a user