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 覆盖面」的偏好,所以不预设。

关联

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions