Skip to content

ADR-0087 别名转换会把「被遮蔽的旧拼法」留在存量元数据里 —— 节点 config 收紧后它从静默丢弃变成执行期硬拒 #4923

Description

@xuyushun441-sys

发现于 #4001 批 9(PR 见下),不在该批范围内,未修。

事实(实测,非阅读推断)

renameConfigKey(packages/spec/src/conversions/walk.ts:235)在规范键已存在时什么都不做:

export function renameConfigKey(node: Dict, from: string, to: string): Dict | null {
  const config = node.config;
  if (!isDict(config)) return null;
  if (!(from in config) || config[from] == null) return null;
  if (config[to] != null) return null; // canonical already wins — nothing to do
  …
}

于是每一条走 renameFlowConfigAliases 的 ADR-0087 D2 转换,在「作者两种拼法都写了」时,把退役的那个键原样留在存量元数据里。这不是猜测——转换自己的 fixture 就把它写成了预期的 after 形状:

  • flow-node-crud-object-alias:after = { objectName: 'task', object: 'ignored' }(registry.ts:931)
  • flow-node-map-flow-alias:after = { collection, flowName: 'per_row', flow: 'ignored' }(registry.ts:1283)
  • flow-node-subflow-flow-alias、flow-node-notify-config-aliases(含 source) 同形

而且这种情况一条 notice 都不发(emit 只在真的改名时调用)。

为什么现在才要紧

批 9 之前:遮蔽下来的旧键在执行期 parse 被 .strip 静默删掉,节点照跑。批 9 把这些 config 契约收成 strict 之后:

  • notify / http / CRUD 四件套 / screen / map —— 这些节点类型在 registerFlow() 就已经被 3b — wire the flow executors to parse() their config, and tighten the undeclared-key warning into an error #4277 的描述符键闸门硬拒了,所以遮蔽键今天就已经是注册期硬错,批 9 没改变它们的可达行为;
  • script / subflow —— schemaless 类,validateNodeConfigKeys 按构造跳过它们。批 9 之后一个存量 { flowName, flow } 的 subflow 节点会在执行期被拒为 guard(不可经 fault 边路由),而它今天是能跑的。

批 9 的实测:三个示例 app 的 build 产物共 160 个 flow 节点、52 个带批 9 契约,0 例命中——所以这不是一个已发生的回归,是一个存量元数据里可能存在、我们无法从仓库内证伪的形状。

需要裁定的是契约,不是实现

两个方向都自洽,该由维护者定:

  • A —— 转换负责删掉被遮蔽的旧键(并发一条 notice)。理由:一个「把旧拼法改写成新拼法」的转换,留着旧拼法就是没转换完;ADR-0087 的承诺是「存量数据继续加载」,而收紧之后「继续加载」要求转换产出契约认得的形状。代价:改的是 ADR-0087 注册表行为,5+ 条 fixture 的 after 与 expectedNotices 都要动,而且它把「作者写了两个名字」这件事重新变回一次静默的择一(只是多一条 notice)。
  • B —— 保持现状,让 strict 拒绝并带处方(批 9 已按这个前提写了 guidance:每条都同时说清「改名」和「删掉死掉的那个孪生键」)。理由:作者写了两种拼法,元数据本身是有歧义的,平台静默择一正是 未知键静默剥离仍是全仓默认:把 #3405 的 strict 收紧从一个 schema 推广到整个可授权面(ADR-0078 完整性闸门) #4001 要杀的形状。代价:一个今天能跑的存量 flow,在 17 上会硬停——而 ADR-0087 的存在就是为了不发生这件事。

倾向 A + 保留 B 的处方:转换把死掉的孪生键删干净(数据在静止态就是规范的),strict 的 guidance 依然留着,给那些不经加载路径直接 parse 契约的调用方。但这条改的是 ADR-0087 注册表的语义,按仓库规矩(「一个已接受的 ADR 到有新 ADR 说话为止都有约束力」)不该由收紧批次顺手带过。

建议动的文件(仅清点,未实施)

文件 动作
packages/spec/src/conversions/walk.ts renameConfigKey / renameKey 在遮蔽情况下删除 from 键并返回新 dict(而不是 null)
packages/spec/src/conversions/registry.ts 5 条转换的 fixture after 半边 + expectedNotices;liftNotifySourceShape 的遮蔽分支同理
packages/spec/src/conversions/conversions.test.ts 现有断言记录的是当前的「留着」行为,需改成断言删除

相关

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