Skip to content

authorable-surface 的 tombstone 门禁可被手编基线绕过 —— 删掉基线行就删掉了证据(#4638 / #4643 已两次这样过绿) #4650

Description

@os-zhuang

症状

packages/spec/scripts/build-schemas.ts 的可作者化面 ratchet 检查 (a)(约 L408–428)本意是:可作者化 key 不许无声消失,因为这些 schema 不是 .strict(),Zod 会静默 STRIP 未知键 —— 作者继续写就得到一次干净的 parse 和一个永不生效的设置(#3733、ADR-0104)。错误文案要求走 tombstone:retiredKey() + D2 conversion + D3 chain step + major changeset。

但检查是这样做的:

const prev = new Map(surfaceDoc.keys.map(...));         // ← 读**磁盘上**的 authorable-surface.json
const vanished = [...prev.keys()].filter((k) => !currentKeys.has(k));
if (vanished.length > 0) { /* 报错 + process.exit(1) */ }

surfaceDoc 读的是同一个 commit 里可以被随手改掉的基线文件。把要删的 key 从 authorable-surface.json 里手工删掉,prev 里就没有它,vanished 为空,门禁静默通过 —— 删掉基线行就是删掉证据

文件自己的 description 与错误文案都写着:

A tombstone that has aged out (~two majors) is the ONE legitimate reason to delete a line here — do it in the same PR, deliberately.

但没有任何东西校验这一条。 基线行的删除既不要求对应 [RETIRED] 标记存在过,也不要求它已 aged out,更不要求有 conversion 登记。

已发生两次

PR 删除的 key tombstone conversion / migration 结果
#4638(C3) ui/Notification:*ui/NotificationConfig:*system/NotificationConfig:* 绿
#4643(C4) identity/Session:* 全部 10 个 绿

复核 #4643 的 commit 21676eb5d:authorable-surface.json 里 10 行 identity/Session:* 被直接删除,src/conversions/src/migrations/ 零改动

这两次的实质是否有害?

倾向于无害,但不该被当先例:

  • identity/Session 是 DB 行 / 运行时形状,ui/Notification 是 toast 实例形状 —— 都不是作者在元数据文件里手写的类型,三仓 import 级扫描也已证实零消费方。
  • 更根本的问题是 authorable-surface.json 过度收集:它记录每个被发出的 schema 的全部 properties,包括 api/SessionResponse:successapi/SessionResponse:meta 这类 REST 信封字段 —— 这些无论如何都不是「metadata author 可写」的东西。文件的名字比它的内容强。

所以真正的缺口有两个,建议分开处置。

建议

(1) 堵住捷径。 让基线行的删除必须自证合法。最小实现:检查 (a) 之外再加一条 —— 对比 git show HEAD:packages/spec/authorable-surface.json 与工作树版本,任何被删除的行必须满足「上一版本带 [RETIRED] 标记」且「其 surface 已在 CONVERSIONS_BY_MAJOR / MIGRATIONS_BY_MAJOR 登记过、且登记的 major 距当前 ≥ 2」。不满足就红,错误信息指向本单。

注意实现时别把 (a) 的现有语义弄反:(a) 防的是形状变了而基线没动,新检查防的是基线动了而形状没有正当理由 —— 两者都需要。

(2) 收窄收集面。 ratchet 应只记录从真作者面可达的 schema(object / field / flow / view / connector / plugin 等作者手写的元数据类型,及其传递引用),而不是每个被 gen:schema 发出的 schema。否则「可作者化」这个判定在评审时不可用 —— 每次都要人肉判断某个 key 是不是真的作者面,而这正是 #4638 / #4643 两次都得靠 reviewer 直觉的原因。

(2) 比 (1) 影响面大,可能需要先确定「真作者面根集合」的定义,建议单独排期;(1) 是可以立刻落的窄修。

影响范围

关联

#4535(双源清账主单,v17 重切一节记录了同一发现)、#3733#3855、ADR-0059 §5、ADR-0087、ADR-0104

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions