Skip to content

build-schemas.ts 检查 (b) 用叶名匹配 conversion surface —— 无关簇的 .type 就能让一个 tombstone 冒充「已登记迁移」 #4659

Description

@os-zhuang

#4658(双源清账 C6)的路线定价实验里掉出来的范围外发现,不在 #4658 的 PR 里修,单独立案。与 #4650 同族(都是 authorable-surface 门禁的自我校验漏洞)。

事实

packages/spec/scripts/build-schemas.ts 检查 (b)(「live → retired 必须有登记的迁移」)这样匹配:

const surfaces = registeredRetirementSurfaces().flatMap((s) => s.split(' / '));
const unregistered = newlyRetired.filter(
  (k) => !surfaces.some((s) => s.endsWith('.' + k.split(':')[1])),
);

k 形如 automation/Event:type,k.split(':')[1] 只取叶名 type,再拿它跟全部 major 的所有 conversion surface 做 endsWith。匹配完全不看 key 属于哪个 def —— 任何一条无关的 conversion 只要 surface 以同名叶子结尾,就把这个 tombstone 判为「已登记」。

实测证据

#4658 的定价实验里,我把 src/automation/state-machine.zod.tsEventSchema 两个 key 同时改成 retiredKey(...),没有新增任何 conversion。pnpm check:authorable-surface 的输出:

❌ 1 key(s) were tombstoned with no registered migration:
     - automation/Event:schema

只报了 schemaautomation/Event:type 静默通过 —— 因为 CONVERSIONS_BY_MAJOR[11]flowNodeHttpRenamesurface: 'flow.node.type'.type 结尾。两者毫无关系:一个是 flow 节点的 type,一个是 automation 状态机事件的 type

为什么要紧

检查 (b) 的整个目的是保证退休进入 spec-changes.json / upgrade guide / os migrate meta(ADR-0087 D4)。叶名匹配让这个保证对常见叶名整体失效:typenameconfigfilterschema 之外的 objectNameflowName 等 surface 已在册,任何以它们结尾的 key 都能零迁移文档退休,门禁全绿,消费者只能靠失败才知道。

这与 #4650 是同一类问题:门禁的证据链自己不设防。#4650 是「删基线行没人校验」,本单是「登记检查可被无关条目满足」。

建议处置(需维护者裁决,故不自行动手)

把匹配从叶名收紧到定位到 def 的路径。两个方向:

  1. surface 里带上 def。让 conversion 的 surface${defKey}:${name} 可判定地对应(或加一个显式的 retires: ['automation/Event:type'] 字段),检查 (b) 改成精确集合匹配。最严格,但要回填现有 conversion。
  2. 保留 surface 的散文形态,另加一个显式登记表。新增 RETIRED_KEYS_BY_MAJOR: Record<number, string[]>,值就是 ${defKey}:${name},检查 (b) 只认这张表。改动小,且「declared = enforced」。

倾向 2:它让登记这件事从「碰巧字符串对上」变成作者必须写下确切的 key,正是 AI 生成的元数据最容易蒙混的那一环。

关联:#4650(基线手编漏洞)、#4658(发现现场)、#4535、ADR-0087、ADR-0104

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions