Skip to content

spec 门禁盲区:可作者化 key 的「默认值 / 约束」变更不被任何 gate、tombstone 或 conversion 记录(#4650 / #4659 同族) #4666

Description

@os-zhuang

#4661(#4535 C8 RetryPolicy 收敛)里分出来的独立发现,已实证,不是推测。与 #4650(基线可手编)、#4659(检查 (b) 按 leaf name 匹配)是同一族:门禁看起来绿,但它没在看你以为它在看的东西。

结论

packages/spec/scripts/build-schemas.ts 的可作者化面门禁 只比较 key 的集合。同一个 key 的 默认值 (.default())约束 (.min() / .max() / .positive()) 变更,对它完全不可见 —— 而这类变更恰恰能静默改变已部署元数据的运行时行为

三条记录通道全部漏掉它:

通道 是否记录默认值变更
authorable-surface.json 比对(检查 (a) vanish) ❌ 只有 key 名
retiredKey() tombstone(检查 (b)) ❌ 只在 live → retired 时触发
spec-changes.json / upgrade guide(ADR-0087 D4) ❌ 是 conversion + migration registry 的投影,而默认值变更不必然带 conversion

也就是说:一个 PR 可以把 job.retryPolicy.maxRetries 的默认值从 3 改成 0(让所有依赖默认值的存量 job 静默停止重试),而全部门禁绿、基线不动、没有任何一行 tombstone 或 conversion 记录这件事。

实证(#4661 的 sabotage 验证)

#4661 的分支上,把 shared/retry-policy.zod.tsmaxRetries 默认值从 0 改成 3,只改这一个字符,然后跑门禁:

$ pnpm check:authorable-surface

✅ Generated bundled schema: objectstack.json (1686 definitions)
✅ Successfully generated 1703 schemas.

绿。 同一处改动被 #4661 手写的运行时 pin 抓住了:

$ npx vitest run src/shared/retry-policy.test.ts

 × pins the opt-in defaults that no gate can observe
 FAIL  ... > pins the opt-in defaults that no gate can observe
 AssertionError: expected 3 to be +0 // Object.is equality

即:今天唯一能挡住这类变更的,是作者自己想起来手写一个 pin 测试。 没有任何机制强制它存在。

为什么这比听上去更糟

  1. 它专挑最危险的语义。 默认值决定「作者没写这个 key 时会发生什么」。retry 次数、超时、enabledrequired、各种 allow* —— 这些的默认值翻转就是安全/可靠性事件,而且是静默的。
  2. AI 写的元数据大量依赖默认值。 省略 key 是 LLM 生成元数据的常态,所以默认值的实际覆盖面远大于人手写的时代。
  3. 约束收紧同理。 #4661maxRetries 加了 max(10)backoffMultiplier 加了 min(1),存量 maxRetries: 20 的 job 会硬拒。硬拒是响亮的、可接受的,但门禁一样没记录 —— 是人写了 semantic 迁移条目才留下痕迹。
  4. ADR-0087 的机器不建模它。 D2 conversion 的 ConversionApplication{from, to, path} —— 描述的是值的重写,没有「这个 key 的默认语义变了」的表达形式。

可能的处置方向(未裁决,列出来供讨论)

  • A. 把默认值/约束纳入 authorable-surface.json 的比对。 每个 key 除了名字再记一个形状指纹(默认值 + 约束的规范化摘要)。变更即失败,要求显式确认。
  • B. 只对「默认值存在性/取值」做指纹,不管约束。 更窄、更少噪音,覆盖最危险的那一类。
  • C. 不改门禁,改流程:要求任何 .default() 变更必须带 semantic 迁移条目,用 lint 规则(AST)检查 diff。
    • 好处:噪音低。代价:靠 diff 检测,git 语境下比 ratchet 脆。
  • D. 什么都不做,只把「默认值变更必须写进 changeset」写进 AGENTS.md。 最便宜,但正是本 issue 想指出的「靠自觉」。

倾向 A 或 B —— 与 #4650 要加的可达性窄例外是同一批门禁加固,可以一起做。但选哪个取决于维护者对「ratchet 噪音 vs 覆盖面」的偏好,所以不预设。

关联

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions