Uh oh!
There was an error while loading. Please reload this page.
fix(app-shell): flow simulator evaluates a { dialect, source } edge guard (#3216) - #3217
Conversation
… guard (#3216) Two readers in `previews/simulator/` each hand-rolled `typeof c === 'string'` and returned `undefined` for anything else, so a decision out-edge guarded by the ADR-0089 expression envelope was reported as `Branch has no condition.` and skipped — while the engine evaluates it normally at run time. That envelope is not an exotic spelling: `ExpressionInputSchema` is a `ZodPipe` that rewrites an authored guard string INTO `{ dialect: 'cel', source }`, and `FlowEdgeSchema.condition` is that schema, so the shape the simulator could not read is the shape the platform produces. Both readers now go through `conditionText` — the one reader every other consumer of the field already used (canvas labels, `FlowEdgeInspector`, the Branches<->edges reconciliation) — so "how an edge guard is read" has exactly one answer in this repo. `validateFlowDraft`'s "no default branch" warning follows: a decision whose guards are envelopes was silently exempt from it. `SimEdge.condition` mirrors the spec's `ExpressionInput` by importing it — the last copy of the restatement objectui#3202 removed from `FlowDesignerEdge` — pinned by compile-time assertions in the project CI type-checks. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRJtkgUAaVG11FsJQbvZWA
The latest updates on your projects. Learn more about Vercel for GitHub. |
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
…aiming (#3216) The behavioural suite's `specEdge` helper annotates its return as `SimEdge`, and its JSDoc called that "a compile-time proof". It is not one here: the package tsconfig excludes `**/*.test.ts`, app-shell's test tree is still TEST_DEBT in the type-check ratchet, and vitest erases types — so no `tsc` run reads that annotation. A comment claiming enforcement that nothing performs is the objectui#3009 / objectui#3181 failure mode, in miniature. The claim moves to where it IS compiled: `flow-sim-edge.types.test.ts`, in `tsconfig.typetests.json`, now asserts `z.infer<typeof FlowEdgeSchema>` extends `SimEdge` — an edge the server hands back is a thing the simulator accepts, with no reconciliation and no cast. The helper's JSDoc says plainly that its annotation is a statement, not a check, and points at the pin. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRJtkgUAaVG11FsJQbvZWA
os-zhuang
commented
Aug 2, 2026
补一个第二次提交(
所以把这个断言搬到真的会被编译的地方: 验证: Generated by Claude Code |
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#3216
缺陷
决策节点的一条出边,守卫存成
{ dialect: 'cel', source: 'amount > 10' }时,模拟器在时间线上报Branch has no condition.并跳过这条分支;变量给amount = 20也会落到 default 分支或直接死路,而引擎在真实运行时会正常求值这条守卫。根因是
previews/simulator/里有两份手写的读法,都只认裸字符串:flow-simulator.ts的condStrflow-sim-validate.ts的edgeCondString信封形式一律返回
undefined。为什么信封不是"边角拼写"
@objectstack/spec的ExpressionInputSchema是一个ZodPipe:parse 时会把裸字符串改写成{ dialect: 'cel', source },而FlowEdgeSchema.condition用的正是它。也就是说,模拟器读不了的那种拼写,恰恰是平台自己对每一条作者写下的守卫产出的那种。仓内其它读同一字段的地方(conditionText、validateExpressionClient)一直两种都认——漏掉一条分支的只有模拟器这两处。一个设计时调试器给出的路线和运行时不一致,比没有调试器更糟;而"不静默模拟与运行时不同的语义"正是模拟器自己写在文件头上的契约。
改法:收敛到一个读法
两处都改走
conditionText(previews/flow-canvas-layout.ts),即画布标签、FlowEdgeInspector、inspectors/flow-decision-edges.ts的分支匹配已经在走的那一个。没有新增第三份手写读法——本 PR 之后,"边守卫怎么读"在本仓只剩一个答案,并且把这一点写进了conditionText的 JSDoc(第五份手写拷贝会把这一类缺陷原样带回来)。行为上有两处变化,方向都是向运行时靠拢:
ast、没有source的信封(spec 阶段 M9.2)仍然报 "no condition":确实没有可求值的东西,如实说,而不是伪造一个结果。validateFlowDraft的"决策没有 default 分支"警告原先要求每条出边都是裸字符串守卫,守卫是信封的决策被静默豁免。它一样会死路,所以现在问题面板和画布横幅对这类流程也会给出警告。类型
SimEdge.condition改为import type引入 spec 的ExpressionInput—— 这是 #3202 从FlowDesignerEdge上摘掉的那份重述的最后一份拷贝。原来的string | { source?: string }两个方向同时是错的:dialect的信封,而服务端FlowEdgeSchema直接拒绝这种形状;'dialect' does not exist in type '{ source?: string }')—— 持久化流程真正携带的那唯一一种形状,反倒是你写不出来的那一种。所以是导入而不是重述:手工"修正"只会把漂移挪到下一个漏掉的成员(
ast、meta)上。测试
previews/simulator/__tests__/flow-simulator.test.ts新增decision guards in the spec expression envelope (#3216):边固件不再手写信封,而是把作者输入喂给FlowEdgeSchema.parse—— 断言的就是平台自己产出的形状,固件无法与 spec 漂移。覆盖 issue 正文的复现、"确实求值而不是一律为真"、ast-only 信封仍报 no condition、以及flow-sim-validate少判的那条诊断。previews/simulator/__tests__/flow-sim-edge.types.test.ts,并登记进tsconfig.typetests.json(CI 真的会编译的那个 project)。类型断言之外还有一条只有多余属性检查能观察到的:规范信封写成字面量必须能通过。修复后
Test Files 3 passed (3) / Tests 59 passed (59)。类型 pin 同样是有效的:把flow-sim-types.ts回退到 main 后,tsc -p tsconfig.typetests.json在 4 条断言上报TS2344: Type 'false' does not satisfy the constraint 'true'。验证
已加 changeset(
@object-ui/app-shell: minor—— 行为变更 + 公开类型收紧)。未触碰scripts/check-spec-symbol-derivation.mjs(#3160 正在改该文件),也未触碰content/docs/releases/。🤖 Generated with Claude Code
https://claude.ai/code/session_01PRJtkgUAaVG11FsJQbvZWA
Generated by Claude Code