Skip to content

Design: should FlowNodeSchema parse its ADR-0031 regions, instead of a post-parse pass callers must remember to run? #4415

Description

@os-zhuang

设计讨论,不是 bug —— 现状能工作,问的是它能不能不依赖调用方的记性。接 #4347 / #4381 / #4399 / #4401

现状

FlowSchema.parse 归一化 flow 自己的 nodes[] / edges[],但够不到 region,因为 region 住在 FlowNodeSchema.config 里 —— 一个刻意开放的 z.record#4381 用一道后置 pass 补上:

const flowShell = FlowSchema.parse(converted);
validateControlFlow(flowShell);
const parsed = normalizeControlFlowRegions(flowShell);   // ← 必须记得调

问题

「parse 完拿到的就该是规范形状」是 Zod-First(PD #1)的默认预期。现在多了一条不成文规则:拿到 FlowParsed 之后还得再调一个函数,否则嵌套 region 里的边条件还是裸字符串、节点还没过 .strict()

今天只有一个消费方(registerFlow),所以没出事。但这正是 #4347 那一族缺陷的生成条件 —— 一个新消费方(Studio 发布路径、未来的 MCP 工具、某个批量校验脚本)拿到 FlowParsed 就直接用,就又是一次「看起来解析过了、其实只解析了一半」。

为什么 #4381 当时没做

评估过、刻意没做,理由是 blast radius:

  • FlowNodeSchema.transform() 会把它从 ZodObject 变成 ZodPipe,而它被 form 生成、z.toJSONSchema、以及 FlowRegionSchema / ParallelBranchSchema 反过来引用;
  • lazy-schema.ts 的注释里已经记着 .strict().transform(…) pipe 曾经把 toJSONSchemaseen 表打崩过(ADR-0089 D3a 时 FormFieldSchema / PageComponentSchema 踩过),现在靠一个 _zod facade 兜着;
  • region 是自递归的(FlowNodeSchemaFlowRegionSchemaFlowNodeSchema),transform 里再套一层解析要小心构造期递归。

把这些混进一个 bugfix PR 里,会让那个 PR 的风险说不清 —— 所以当时选了后置 pass,并在函数注释里写明了这笔债。

可选方向(未定)

  1. FlowNodeSchema 自解析 region。 最符合 PD Add metamodel interfaces for ObjectQL/ObjectUI contract #1,一次解决所有消费方。代价见上,需要先确认 toJSONSchema / form 生成不受影响。
  2. FlowSchema 层挂 transform。 比 1 影响面小(FlowNodeSchema 保持 ZodObject),但嵌套递归得自己写,且绕过 FlowSchema 直接 FlowNodeSchema.parse 的调用方仍然漏。
  3. 维持现状 + 加护栏。 比如让 collectFlowGraphs / runRegion 在遇到未归一化的 region 时 warn,把「忘了调」变成有声音的。成本最低,但没有消除那条不成文规则。

倾向 1,前提是先量清楚 toJSONSchema 的影响 —— 不该在没有这个数之前动手,这和 #4389 「先量再改」是同一个道理。

相关

#4336 的建议修复 #3(「给 node config 上类型」)是邻近但不同的一件事:那条针对表达式归一化够不到 config.condition,这条针对 region 子结构。两者如果都做,大概率是同一个机制的两次应用。

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions