维护者速读
事情:设计方案第 7 章的状态转换守卫写着「生效 → 已终止:终止日期与原因必填」,但第 3 章的字段清单里没有「终止原因」这个字段。卡 02 实现状态机时只能盖终止日期,原因无处可存。
为什么现在问你:修法本身很小(加一个字段),但它要改 DESIGN.md §03,而仓库规矩是代码 PR 不得改 §01–§04,必须先有你的裁定。
选项:
- A(推荐):加一个「终止原因」文本字段,只在终止时必填,其余时候不出现在表单上;在卡 09 落地,同时补 §03 字段清单。
- B:把守卫里的「原因必填」去掉,终止原因写在合同摘要里。
代价:A 多一个字段,多一行设计文档修订;B 不加字段,但终止合同将没有结构化的原因记录,审计时只能翻自由文本。
你要做的:回一个字母,A 或 B。
Context
DESIGN.md §03 state-machine table:
| 从 |
到 |
守卫 |
| active |
terminated |
closed_at 与终止原因必填 |
The §03 field list for clm_contract declares closed_at but no reason field. Card 02 (#2, PR #5) implemented the guard as far as it could — it stamps closed_at and refuses without it — and reported the gap rather than inventing a field outside the pinned list.
Governing text: DESIGN.md §03 (the guard row above, and the clm_contract field list); AGENTS.md → Delivery process → "Files a code PR never touches: … DESIGN.md §01–§04 without a needs-user-decision first".
Options
A — declare termination_reason (textarea, requiredWhen the status is terminated), amend the §03 field list in the same change, implement in card 09 alongside F13/F14. The field never appears on the intake form; it is asked exactly once, at termination.
B — drop "原因必填" from the guard wording. The reason, when someone bothers to record it, lives in summary.
四棱分析
实际业务需求 — A. 提前终止的合同是法务与审计最常回头查的一类记录,而「为什么终止」正是他们问的第一个问题:是对方违约、是我方不再需要、还是双方协商。设计方案自己的守卫已经写了「原因必填」,说明写方案时就认定这是业务需要,不是事后加戏。B 把这个答案散进自由文本,等于放弃了按原因统计终止合同的能力。
项目长远合理性 — A。当前设计文档自相矛盾:守卫引用了一个字段清单里不存在的字段。两个选项都能消除矛盾,但方向相反:A 让实现追上已声明的意图,B 削弱意图去迁就实现。契约优先的规矩是以协议为基准、改代码对齐,A 是那个方向。
防 AI 写错 — A,而且这一棱最不平衡。requiredWhen 是运行期强制的结构性约束:任何写入者,无论是人、流程还是 AI,都不可能在不给原因的情况下终止一份合同。B 依赖的是约定(「记得写进摘要」),而约定恰恰是错误能静默累积的地方 —— 这正是「声明即强制,绝不让 AI 声明一个运行时不兑现的能力」要防的形状。今天守卫说必填而系统不存原因,就已经是一个不兑现的声明。
创业阶段不扩散 — 大致中性,略偏 A。A 只加一个字段,且这个字段是已确认的 V1.0 基准自己要求的,不是新增能力面,因此不构成需求扩散。B 表面上更紧,但它删掉的是一条已声明的能力,属于反方向的基准变更,同样需要你拍板。
结论:四棱同向 A,第三棱(防错)差距最大。推荐 A。
Impact if deferred
None blocking. Card 02 shipped with closed_at only; card 09 (F13/F14, expiry and archive) is where the terminate path gets its flows and is the natural place to land the field. Answering before card 09 is dispatched avoids a second pass over the same hook.
维护者速读
事情:设计方案第 7 章的状态转换守卫写着「生效 → 已终止:终止日期与原因必填」,但第 3 章的字段清单里没有「终止原因」这个字段。卡 02 实现状态机时只能盖终止日期,原因无处可存。
为什么现在问你:修法本身很小(加一个字段),但它要改
DESIGN.md§03,而仓库规矩是代码 PR 不得改 §01–§04,必须先有你的裁定。选项:
代价:A 多一个字段,多一行设计文档修订;B 不加字段,但终止合同将没有结构化的原因记录,审计时只能翻自由文本。
你要做的:回一个字母,A 或 B。
Context
DESIGN.md§03 state-machine table:closed_at与终止原因必填The §03 field list for
clm_contractdeclaresclosed_atbut no reason field. Card 02 (#2, PR #5) implemented the guard as far as it could — it stampsclosed_atand refuses without it — and reported the gap rather than inventing a field outside the pinned list.Governing text:
DESIGN.md§03 (the guard row above, and theclm_contractfield list);AGENTS.md→ Delivery process → "Files a code PR never touches: …DESIGN.md§01–§04 without aneeds-user-decisionfirst".Options
A — declare
termination_reason(textarea,requiredWhenthe status isterminated), amend the §03 field list in the same change, implement in card 09 alongside F13/F14. The field never appears on the intake form; it is asked exactly once, at termination.B — drop "原因必填" from the guard wording. The reason, when someone bothers to record it, lives in
summary.四棱分析
实际业务需求 — A. 提前终止的合同是法务与审计最常回头查的一类记录,而「为什么终止」正是他们问的第一个问题:是对方违约、是我方不再需要、还是双方协商。设计方案自己的守卫已经写了「原因必填」,说明写方案时就认定这是业务需要,不是事后加戏。B 把这个答案散进自由文本,等于放弃了按原因统计终止合同的能力。
项目长远合理性 — A。当前设计文档自相矛盾:守卫引用了一个字段清单里不存在的字段。两个选项都能消除矛盾,但方向相反:A 让实现追上已声明的意图,B 削弱意图去迁就实现。契约优先的规矩是以协议为基准、改代码对齐,A 是那个方向。
防 AI 写错 — A,而且这一棱最不平衡。
requiredWhen是运行期强制的结构性约束:任何写入者,无论是人、流程还是 AI,都不可能在不给原因的情况下终止一份合同。B 依赖的是约定(「记得写进摘要」),而约定恰恰是错误能静默累积的地方 —— 这正是「声明即强制,绝不让 AI 声明一个运行时不兑现的能力」要防的形状。今天守卫说必填而系统不存原因,就已经是一个不兑现的声明。创业阶段不扩散 — 大致中性,略偏 A。A 只加一个字段,且这个字段是已确认的 V1.0 基准自己要求的,不是新增能力面,因此不构成需求扩散。B 表面上更紧,但它删掉的是一条已声明的能力,属于反方向的基准变更,同样需要你拍板。
结论:四棱同向 A,第三棱(防错)差距最大。推荐 A。
Impact if deferred
None blocking. Card 02 shipped with
closed_atonly; card 09 (F13/F14, expiry and archive) is where the terminate path gets its flows and is the natural place to land the field. Answering before card 09 is dispatched avoids a second pass over the same hook.