Skip to content

build-schemas.tszodShapeOfz.preprocess 走错管道方向(#4488 已在 check-liveness 修过的同一个盲点) #5317

Description

@os-zhuang

发现于 #5074 实施途中(observation-class:今天没有用户能撞到,但盲点是真的,且会随下一个 preprocess 根注册复发)。

事实

packages/spec/scripts/build-schemas.ts:687:

if (def.type === 'pipe' && def.in instanceof z.ZodType) return zodShapeOf(def.in, depth + 1);

管道有两种,授权面在相反的一侧:

  • a.transform(fn) —— 授权面是 IN(a 是被接受的输入形状);
  • z.preprocess(fn, schema) —— IN 是那个 transform,授权面是 OUT

这里无条件取 def.in,所以对 preprocess 根拿到的是 transform,zodShapeOf 返回 null

这个盲点在本仓已经有过一次实例并被修过:scripts/liveness/check-liveness.mts:196unwrap 带着 #4488 的注释,说的正是同一件事 —— TranslationItemSchema 的 retired-dialect preprocess 守卫(#3778)让 translation 一度「walked to no shape,ungovernable」。那次只修了 liveness 一处;build-schemas.ts 这处没跟上。

#5074 又在两处碰到同一个盲点(kernel/metadata-authoring-lint.tssystem/metadata-form-zod-reconciliation.test.ts),都在 #5074 的 PR 里按 #4488 的写法修了。build-schemas.ts 故意留着没动,因为它的行为改变会移动生成基线(见下),超出 #5074 的范围。

为什么归为 observation-class 而不是缺陷

zodShapeOf 在这里只喂 computeSurfaceReachabilityderived-clone 桥,而:

  • BFS 的 visited 闭包走的是 zodChildSchemas,它递归遍历 def 的所有值(含 def.out),所以可达性本身不受影响;
  • 取不到 shape 时该函数 return 'root-graph'(fail closed,宁可多要一个 tombstone 也不静默放宽)。

所以当前表现是保守,不是错答。今天没有已知的错误产物。

但值得修

  1. 它是「同一个盲点、第二次出现」—— 活性账本覆盖 worklist:9 个已注册 metadata type 仍未治理(#4487 建立闸门后的剩余债务) #4488 的教训只落在一个文件里,没有落成规则。三处独立复发(liveness/活性账本覆盖 worklist:9 个已注册 metadata type 仍未治理(#4487 建立闸门后的剩余债务) #4488、authoring-lint 与 form-reconciliation/ViewItemSchema 同时是授权形状和 Studio 往返的 wire 成员 —— 拆成两个 schema 还是保持宽松?(挡住 #4001 批 18 最后 2 站点) #5074)说明这是模式,不是巧合。
  2. translation 今天就是 preprocess 根注册,所以这条分支现在就在走错方向,只是后果被 fail-closed 兜住了。

修的时候注意

改成 #4488 的写法(IN 侧解开后若是 transform 则改走 OUT)会让 translation 这类 preprocess 根第一次拿到真实 shape,derived-clone 桥因此可能新增 bridge 项,authorable-surface.json / 可达性判定有可能移动。所以这不是一行改动就完事:要跑 pnpm --filter @objectstack/spec check:generated,并逐条解释产生的 diff(尤其是任何从 root-graph 变成 derived-clone 的条目 —— 那正是 #5056 提醒过的、会把死形状标成可达的桥)。

参考实现:packages/spec/scripts/liveness/check-liveness.mts:191-205
同一 PR 里可以顺手加一条断言,把「preprocess 根能被解出 shape」钉住,免得第四次复发。

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions