发现于 #4001 批 18(ui/view.zod.ts),范围外,未在该 PR 修。
缺陷
packages/spec/scripts/lib/strictness-ledger.ts 的 postureOf() 第一行就对 campaign 自己的 helper 惯用法短路:
function postureOf(call: ts.CallExpression, idiom: string): Posture {
if (idiom === 'strictObject' || idiom === 'z.strictObject') return 'strict';
// ...只有走到这里才会遍历 .strict() / .passthrough() / .catchall() 链
对 z.object(...) 它会正确地走链;对 strictObject(...) 它直接返回 strict,不看后面挂了什么。于是 strictObject(...).passthrough() —— 一个运行期开放的形状 —— 在台账里被记为 strict。
实测(非阅读推断)
GanttConfigSchema.parse({ startDateField:'a', endDateField:'b', titleField:'c', lockField:'x' })
=> { startDateField:'a', endDateField:'b', titleField:'c', lockField:'x' } // lockField 原样通过
TreeConfigSchema.parse({ labelField:'a', parentField:'b', zzz:1 })
=> { parentField:'b', labelField:'a', zzz:1 } // zzz 原样通过
两者的 .passthrough() 都是刻意的(注释写明:让 renderer-ahead 的 config 旋钮到达 plugin-gantt / plugin-tree,不必等 spec 发版),objectui 侧也有对应记录(types/src/zod/objectql.zod.ts 的注释直接引用「the same reason the spec puts it on GanttConfigSchema/TreeConfigSchema」)。问题不在这两个 schema,在读它们的尺。
影响面(AST 精确统计,全 packages/spec/src)
恰好 2 处,都在 ui/view.zod.ts:GanttConfigSchema、TreeConfigSchema。
⚠️ 统计这个数字本身踩了一次坑,值得记:用「站点行往后扫 N 行找 }).passthrough()」的正则法得到 11,因为窗口会捞到后面别的站点的链尾。改成真正沿 parent 走链的 AST 判定后是 2。别用行窗口复核这条,会得到一个吓人的假数。
为什么值得修(而不是「只差 2 个,无所谓」)
处方
postureOf() 对 strictObject / z.strictObject 应当以 'strict' 为初值继续走链,而不是提前返回 —— 与 z.looseObject 同理(它也是提前返回 'passthrough',同样挡住了 .strict() 覆写,虽然目前全仓无实例)。
let posture: Posture =
idiom === 'strictObject' || idiom === 'z.strictObject' ? 'strict'
: idiom === 'z.looseObject' ? 'passthrough'
: 'strip';
// …然后一律走链,让链上最后一个显式调用赢
修的时候请先证红:把 GanttConfigSchema 的期望姿态钉成 passthrough,确认 scripts/strictness-ledger.test.ts 在修之前是红的。改完 ui/ 的 strict 计数会 −2、passthrough 计数 +2,台账表头需要同步(strip 计数不变,所以 remaining-strip 那张双向表不受影响)。
顺带值得判一下:这两处是否该在台账里单列一类。它们既不是 authorable(已关)也不是留开的 strip —— 是刻意开放的父 + 已关闭的子块(批 18 关掉了 GanttConfig.tooltipFields,父仍开放)。现有四类(real door / no gate / no door / wire)都不完全贴。
发现于 #4001 批 18(
ui/view.zod.ts),范围外,未在该 PR 修。缺陷
packages/spec/scripts/lib/strictness-ledger.ts的postureOf()第一行就对 campaign 自己的 helper 惯用法短路:对
z.object(...)它会正确地走链;对strictObject(...)它直接返回strict,不看后面挂了什么。于是strictObject(...).passthrough()—— 一个运行期开放的形状 —— 在台账里被记为 strict。实测(非阅读推断)
两者的
.passthrough()都是刻意的(注释写明:让 renderer-ahead 的 config 旋钮到达 plugin-gantt / plugin-tree,不必等 spec 发版),objectui 侧也有对应记录(types/src/zod/objectql.zod.ts的注释直接引用「the same reason the spec puts it onGanttConfigSchema/TreeConfigSchema」)。问题不在这两个 schema,在读它们的尺。影响面(AST 精确统计,全
packages/spec/src)恰好 2 处,都在
ui/view.zod.ts:GanttConfigSchema、TreeConfigSchema。}).passthrough()」的正则法得到 11,因为窗口会捞到后面别的站点的链尾。改成真正沿parent走链的 AST 判定后是 2。别用行窗口复核这条,会得到一个吓人的假数。为什么值得修(而不是「只差 2 个,无所谓」)
ui/的 strict 读数里有 2 个其实是 passthrough。战役的排期与「还剩多少」都读这个数。time-relative-trigger折行写法计成 0、零站点文件被覆盖检查跳过)是同一个形状,只是这次盲区落在 posture 而不是 count 上。fix(spec): #4001 重测 —— 实测剩余站点清单,并修好测量它的那把尺 #4852 把idiomOf/nameOf都重建在 AST 上了,postureOf是同一次重建里唯一保留了短路的那个。处方
postureOf()对strictObject/z.strictObject应当以'strict'为初值继续走链,而不是提前返回 —— 与z.looseObject同理(它也是提前返回'passthrough',同样挡住了.strict()覆写,虽然目前全仓无实例)。修的时候请先证红:把
GanttConfigSchema的期望姿态钉成passthrough,确认scripts/strictness-ledger.test.ts在修之前是红的。改完ui/的 strict 计数会 −2、passthrough 计数 +2,台账表头需要同步(strip 计数不变,所以 remaining-strip 那张双向表不受影响)。顺带值得判一下:这两处是否该在台账里单列一类。它们既不是
authorable(已关)也不是留开的 strip —— 是刻意开放的父 + 已关闭的子块(批 18 关掉了GanttConfig.tooltipFields,父仍开放)。现有四类(real door / no gate / no door / wire)都不完全贴。