做 #3202(收紧设计器边类型)时越界发现,未在该 PR 里顺手改 —— 它是行为变更,不是类型收紧。
缺陷
packages/app-shell/src/views/metadata-admin/previews/simulator/ 里有两个读边守卫的地方,都只认裸字符串,信封形式一律返回 undefined:
// flow-simulator.ts:46
const condStr = (c: SimEdge['condition']): string | undefined => (typeof c === 'string' ? c : undefined);
// flow-sim-validate.ts:18
const edgeCondString = (c: SimEdge['condition']): string | undefined =>
typeof c === 'string' ? c : undefined;
于是 flow-simulator.ts:281 处,一条守卫写成 { dialect: 'cel', source: 'amount > 10' } 的决策出边,在模拟时间线上报的是 Branch has no condition.,并被跳过 —— 模拟结果会走到 default 分支或直接停住,而引擎在真实运行时会正常求值这条守卫。flow-sim-validate.ts:154 的诊断也会因此少判一类。
为什么是真 bug 而不是设计
仓内其它每一个读同一个字段的地方都接受两种拼写:
previews/flow-canvas-layout.ts 的 conditionText —— 字符串或 { source } 都读(画布标签、FlowEdgeInspector 输入框、flow-decision-edges 的分支匹配都走它);
inspectors/expression-validate.ts 的 validateExpressionClient —— 注释就写着「Accept a bare string or an Expression envelope」;
inspectors/flow-decision-edges.test.ts 里已有一条用例把 { dialect: 'cel', source } 的边条件当作正常存量形状匹配。
而 @objectstack/spec 的 ExpressionInputSchema 是一个 ZodPipe:裸字符串在 parse 时就被规范化成 { dialect: 'cel', source } 信封,信封才是 spec 眼里的规范持久化形式(FlowEdgeSchema.condition 用的正是它)。也就是说,模拟器读不了的那种拼写,恰恰是平台自己产出的那种。
所以这不是「模拟器故意只支持简写」,而是两个读函数漏了一条分支 —— 与 #3202 同一族:一个过窄/过宽的本地读法与 spec 不一致。
建议修法
两处都改为走同一个读法(conditionText 已经是仓内的既有实现,或抽一个共享的 expressionSource(c)),不要各自再写第三、第四份 typeof c === 'string'。
顺带:flow-sim-types.ts:30 的 SimEdge.condition 仍写作 string | { source?: string } —— 那是 #3202 收紧掉的、spec 会拒绝(缺 dialect)的形状的最后一份复制。修这条时一并镜像 spec 的 ExpressionInput(import type 即可,别再重述一遍)。
验证建议
一条决策节点的出边,守卫存成 { dialect: 'cel', source: 'amount > 10' },变量给 amount = 20:期望模拟器选中该分支;当前会得到 Branch has no condition.。
相关
做 #3202(收紧设计器边类型)时越界发现,未在该 PR 里顺手改 —— 它是行为变更,不是类型收紧。
缺陷
packages/app-shell/src/views/metadata-admin/previews/simulator/里有两个读边守卫的地方,都只认裸字符串,信封形式一律返回undefined:于是
flow-simulator.ts:281处,一条守卫写成{ dialect: 'cel', source: 'amount > 10' }的决策出边,在模拟时间线上报的是Branch has no condition.,并被跳过 —— 模拟结果会走到 default 分支或直接停住,而引擎在真实运行时会正常求值这条守卫。flow-sim-validate.ts:154的诊断也会因此少判一类。为什么是真 bug 而不是设计
仓内其它每一个读同一个字段的地方都接受两种拼写:
previews/flow-canvas-layout.ts的conditionText—— 字符串或{ source }都读(画布标签、FlowEdgeInspector输入框、flow-decision-edges的分支匹配都走它);inspectors/expression-validate.ts的validateExpressionClient—— 注释就写着「Accept a bare string or an Expression envelope」;inspectors/flow-decision-edges.test.ts里已有一条用例把{ dialect: 'cel', source }的边条件当作正常存量形状匹配。而
@objectstack/spec的ExpressionInputSchema是一个ZodPipe:裸字符串在 parse 时就被规范化成{ dialect: 'cel', source }信封,信封才是 spec 眼里的规范持久化形式(FlowEdgeSchema.condition用的正是它)。也就是说,模拟器读不了的那种拼写,恰恰是平台自己产出的那种。所以这不是「模拟器故意只支持简写」,而是两个读函数漏了一条分支 —— 与 #3202 同一族:一个过窄/过宽的本地读法与 spec 不一致。
建议修法
两处都改为走同一个读法(
conditionText已经是仓内的既有实现,或抽一个共享的expressionSource(c)),不要各自再写第三、第四份typeof c === 'string'。顺带:
flow-sim-types.ts:30的SimEdge.condition仍写作string | { source?: string }—— 那是 #3202 收紧掉的、spec 会拒绝(缺dialect)的形状的最后一份复制。修这条时一并镜像 spec 的ExpressionInput(import type即可,别再重述一遍)。验证建议
一条决策节点的出边,守卫存成
{ dialect: 'cel', source: 'amount > 10' },变量给amount = 20:期望模拟器选中该分支;当前会得到Branch has no condition.。相关
id—— 这才是「设计器产出、spec 拒绝」的真实形状 #3202(设计器边类型镜像 spec 的ExpressionInput;本条是同一族的最后一处,但因为要改求值行为而单列)