Skip to content

fix(spec,service-automation): registerFlow's remaining validators cover structured regions (#4389) - #4399

Merged
os-zhuang merged 1 commit into
mainfrom
claude/loving-rubin-wd2qq0
Jul 31, 2026
Merged

fix(spec,service-automation): registerFlow's remaining validators cover structured regions (#4389)#4399
os-zhuang merged 1 commit into
mainfrom
claude/loving-rubin-wd2qq0

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #4389. 接 #4347 / #4381

问题

#4347 修掉了「元数据随嵌套深度而变」的转换谓词校验两半。registerFlow 里还剩三个校验器只遍历 flow.nodes —— ADR-0031 容器把整张子图放在自己开放的 config 里,于是它们各自都在「只校验了一部分、却按整个 flow 汇报」。

先量,再改

两个是 hard-fail。扩大一个会拒绝的校验器的遍历范围是行为变更,不是 bugfix —— 所以先测量,而不是先动手。

测量方式是把三个 example app 的全部 flow 通过真正的 registerFlow 注册(转换 + parse + region 归一化都走完),再用 collectFlowGraphs 取出每个非顶层 graph,对它跑各校验器自己的代码(私有方法直接调用,不重写检查逻辑):

━━━ examples/app-crm        flows 1/1     region graphs 0
━━━ examples/app-showcase   flows 29/29   region graphs 9
━━━ examples/app-todo       flows 4/4     region graphs 0

━━━ TOTAL
    validateControlFlow      0
    validateNodeTypes        0
    validateNodeConfigKeys   0
    validateNodeInputSchemas 0

0 新增违规。所以三个校验器按现有严重级别直接落地,不需要走 #4059#4277 那条 warn-then-error 阶梯。

样本确实薄(9 个 region graph / 9 个 region 节点),但风险不对称地小,理由见下。

改动

validateControlFlow 递归(hard-fail)

嵌套在别的容器 region 里的容器,注册期从来没被校验过 —— 它会一路走到运行时,由 runRegionfindRegionEntry迭代中途抛出,此时外层 loop 已经开跑、副作用已经落地。这正是 ADR-0031 的「reject the malformed before it can run」要避免的。

这一条不可能弄坏能正常工作的 flow:新被拒绝的东西本来就注定在执行时抛异常,只是从「跑到一半炸」提前成「注册期拒绝」。

顺带补上了嵌套 region 的环检测 —— region body 的环是由 analyzeRegion 查的(不是 detectCycles),所以这次递归同时把那个洞也关了,detectCycles 不需要单独改。

validateNodeTypes 覆盖 region 节点(soft-fail)

零风险。loop body 里的节点和它旁边的节点一样会被执行,漏掉 region 意味着这条「预测 NO_EXECUTOR」的警告恰好在最难定位运行时失败的那批节点上失声。

validateNodeConfigKeys 覆盖 region 节点(hard-fail)

visibleIf 正是 #4277 要抓的拼写错误,而把节点挪进 region 就能恢复 #4277 关掉的那种静默。违规信息带上 region:

loop 'sweep' body · node 'w' (create_record): unknown config key `fieldz` at config.fieldz — did you mean `fields`?

不会从容器侧重复报:三个容器 descriptor 都把 region 槽声明成裸的 nodes: { type: 'array' }(无 items),所以 schema 对位遍历到那里就停,不会再下探一遍。有测试钉住这一点。

刻意不改 validateNodeInputSchemas

它同样只走顶层,但不扩,两个理由:

  1. 三个 app 的 159 个 flow 节点里声明 inputSchema 的是 0 个 —— 没有实证读者。
  2. 它拿 config 值的运行时类型和声明类型比(actualType !== paramDef.type)。扩进 region 会让一个在 number 类型槽里写 {var} 模板字符串的 region 节点新失败 —— 而那是活着的作者写法。

给一个已知有误报模式、又没有实证价值的检查扩大范围不划算。这个遍历缺口记在 #4389 上,没有顺手做。

测试

nested-region-parity.test.ts 新增 9 个用例(该文件共 22 个),每组都是对称性对照 —— 同一份坏元数据,一次放顶层、一次放 loop body,判定必须一致:

  • validateNodeConfigKeys:顶层拒 / region 里也拒且带 region 名 / 每个嵌套键只报一次 / 正确的 region 不动
  • validateNodeTypes:顶层警告 / region 里也警告
  • validateControlFlow:顶层容器的畸形 region 被拒 / 嵌套容器的畸形 region 也被拒且路径带两层(loop 'loop' body → loop 'inner' body)/ 良构的嵌套容器不动

非空断言已验证:把两个源文件 stash 掉重新构建后跑,恰好这 4 个嵌套用例失败、18 个通过。

验证

gate 结果
pnpm build 71/71 ✅
pnpm test 132/132 ✅
pnpm typecheck 119/119 ✅
pnpm lint
check:generated 8/8 ✅

另注:三份 region 槽位表

#4388(晚 8 分钟合入)在 packages/lint/src/flow-walk.ts 引入了第三份「region 住在哪里」的声明,和 spec/conversions/walk.tsFLOW_REGION_SLOTSspec/automation/control-flow.zod.tsregionSlotsOf 并存;lint 包内部现在也是两个 walker 并行(validate-expressions.tscollectFlowGraphs,另外四条规则用 flow-walk.ts)。

本 PR 没有动它 —— 那是另一条轴上的问题,而且是别人刚落地的模块。会另开 issue 提合并方案。


Generated by Claude Code

…er structured regions (#4389)

#4347 closed the conversion and predicate halves of "metadata behaves
differently depending on how deep it sits". Three validators were left walking
`flow.nodes` only, so the same class stayed open one layer over: an ADR-0031
container keeps a whole sub-graph in its open `config`, and each of these
checked part of the flow while reporting on all of it.

- `validateControlFlow` recurses. A container nested inside another container's
  region was never validated at registration — it reached run time, where
  `runRegion` → `findRegionEntry` throws mid-iteration, after the enclosing loop
  has begun and its side effects have landed. Cannot break a working flow:
  everything newly rejected was already guaranteed to throw on execution. Also
  closes cycle detection over nested regions, since region bodies are
  cycle-checked by `analyzeRegion` here rather than by `detectCycles`.
- `validateNodeTypes` covers region nodes (soft-fail).
- `validateNodeConfigKeys` covers region nodes (hard-fail), with the region in
  each violation. No double-reporting from the container side: all three
  container descriptors declare their region slot as a bare
  `nodes: { type: 'array' }` with no `items`.

Measured before extending the two hard-fail checks, since widening a rejecting
validator is a behaviour change rather than a bugfix: registering every flow in
app-showcase, app-crm and app-todo through the real `registerFlow` and re-running
each validator's own code over all 9 region graphs produced 0 new findings. So
they land at existing severity rather than staged through a warning window.

`validateNodeInputSchemas` is deliberately NOT extended — 0 uses across all 159
example flow nodes, and it compares a config value's runtime type against the
declared one, so extending it would newly fail a region node carrying a `{var}`
template string in a `number`-typed slot. Noted on #4389 instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSBbKMdDgHjGpQdrvQUQXj
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 31, 2026 11:16am

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Jul 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/service-automation, @objectstack/spec.

107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/service-automation, @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/service-automation, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/service-automation, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/service-automation, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/service-automation, @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants