发现于 #5317 实施途中(observation-class:两格今天都被 fail-closed 兜住,没有已知的错误产物)。
packages/spec/scripts/lib/zod-graph.ts 的 zodShapeOf 是 #5317 从 build-schemas.ts 抽出来的;它有三个同族 walker,做的是同一件事(把一个 Zod 节点解到对象 shape):
packages/spec/scripts/liveness/check-liveness.mts 的 unwrap / shapeOf
packages/spec/src/kernel/metadata-authoring-lint.ts 的 unwrap / keyPosture
packages/spec/src/system/metadata-form-zod-reconciliation.test.ts 的 unwrap / keysOf
#5317 已经把管道方向这一格对齐了(z.preprocess 走 OUT)。剩下两格仍然只有 zodShapeOf 缺:
事实 1 —— 没有 union 分支
zodShapeOf 只认 object,遇到 union 返回 null。三个同族 walker 都合并 union 成员的键(check-liveness 的 shapeOf 带着 #3095 的注释,说的正是「一个 metadata type 可能注册一组 shape 的 UNION 而不是单个对象」)。
后果,#5317 实测:view 是注册表里唯一的 z.preprocess 根,其 OUT 是 z.union(#5074 的 console-decoration 剥离)。#5317 把方向修对之后,pipeAuthorableSide(view) 从 transform 变成了 union —— 但 zodShapeOf(view) 依然是 null,因为没有 union 分支。所以「preprocess 根能被解出真实 shape」这句话对 view 今天仍然不成立,方向修对是必要但不充分。
packages/spec/scripts/zod-graph.test.ts 里有一条测试把这个状态诚实钉住了(documents that view's preprocess OUT is a union, so it still derives no shape),加 union 分支的人会先撞到它。
事实 2 —— wrapper 集合缺 prefault
zodShapeOf 的 SHAPE_WRAPPER_TYPES 是 optional / nullable / default / catch / readonly / nonoptional。三个同族 walker 的对应列表都还有 prefault。一个 prefault 包着的对象在 zodShapeOf 下解不出 shape。
#5317 特意没有顺手加:它没被测量过,而 zodShapeOf 喂的是 derived-clone 桥,任何解出更多 shape 的改动都会新增桥项。
为什么是 observation-class 而不是缺陷
两格的后果都落在 computeSurfaceReachability 的 reachableVia,而那里解不出 shape 时 return 'root-graph'(fail closed —— 宁可多要一个 tombstone,不静默放宽)。所以当前表现是保守,不是错答;而且 reachableVia 只在 #4650 删除门禁发现「基线行被删」时才被调用,平时的 gen:schema 根本不走。今天没有用户能撞到。
这两格任何一格补上,都会让更多节点解出 shape,从而给 bridged 表新增 (propName → propSchemaInstance) 项 —— 这正是 #5056 提醒过的、会把死形状标成可达(null → derived-clone)的桥。所以不是加一行就完事:
两格建议放同一个 PR 一起量:它们改的是同一个函数的同一片 unwrap 面,分开做要把上面那套测量跑两遍。
发现于 #5317 实施途中(observation-class:两格今天都被 fail-closed 兜住,没有已知的错误产物)。
packages/spec/scripts/lib/zod-graph.ts的zodShapeOf是 #5317 从build-schemas.ts抽出来的;它有三个同族 walker,做的是同一件事(把一个 Zod 节点解到对象 shape):packages/spec/scripts/liveness/check-liveness.mts的unwrap/shapeOfpackages/spec/src/kernel/metadata-authoring-lint.ts的unwrap/keyPosturepackages/spec/src/system/metadata-form-zod-reconciliation.test.ts的unwrap/keysOf#5317 已经把管道方向这一格对齐了(
z.preprocess走 OUT)。剩下两格仍然只有zodShapeOf缺:事实 1 —— 没有 union 分支
zodShapeOf只认object,遇到union返回null。三个同族 walker 都合并 union 成员的键(check-liveness 的shapeOf带着 #3095 的注释,说的正是「一个 metadata type 可能注册一组 shape 的 UNION 而不是单个对象」)。后果,#5317 实测:
view是注册表里唯一的z.preprocess根,其 OUT 是z.union(#5074 的 console-decoration 剥离)。#5317 把方向修对之后,pipeAuthorableSide(view)从transform变成了union—— 但zodShapeOf(view)依然是null,因为没有 union 分支。所以「preprocess 根能被解出真实 shape」这句话对view今天仍然不成立,方向修对是必要但不充分。packages/spec/scripts/zod-graph.test.ts里有一条测试把这个状态诚实钉住了(documents that view's preprocess OUT is a union, so it still derives no shape),加 union 分支的人会先撞到它。事实 2 —— wrapper 集合缺
prefaultzodShapeOf的SHAPE_WRAPPER_TYPES是optional / nullable / default / catch / readonly / nonoptional。三个同族 walker 的对应列表都还有prefault。一个prefault包着的对象在zodShapeOf下解不出 shape。#5317 特意没有顺手加:它没被测量过,而
zodShapeOf喂的是 derived-clone 桥,任何解出更多 shape 的改动都会新增桥项。为什么是 observation-class 而不是缺陷
两格的后果都落在
computeSurfaceReachability的reachableVia,而那里解不出 shape 时return 'root-graph'(fail closed —— 宁可多要一个 tombstone,不静默放宽)。所以当前表现是保守,不是错答;而且reachableVia只在 #4650 删除门禁发现「基线行被删」时才被调用,平时的gen:schema根本不走。今天没有用户能撞到。修的时候注意(#5056)
这两格任何一格补上,都会让更多节点解出 shape,从而给
bridged表新增 (propName → propSchemaInstance) 项 —— 这正是 #5056 提醒过的、会把死形状标成可达(null→derived-clone)的桥。所以不是加一行就完事:pnpm --filter @objectstack/spec gen:schema+check:generated;derived-clone的条目;build-schemas.ts的zodShapeOf对z.preprocess走错管道方向(#4488 已在 check-liveness 修过的同一个盲点) #5317 的做法:在computeSurfaceReachability末尾临时 dump 全部 def 的reachableVia判定,改前改后各跑一次做 diff。build-schemas.ts的zodShapeOf对z.preprocess走错管道方向(#4488 已在 check-liveness 修过的同一个盲点) #5317 用这个办法量到「管道方向修正只移动 1 条判定、0 条新桥」,分片生成物零 diff。两格建议放同一个 PR 一起量:它们改的是同一个函数的同一片 unwrap 面,分开做要把上面那套测量跑两遍。