Skip to content

feat(automation,spec): 流程执行器 parse() 自己的 config,未声明键在注册时报错 (#4277) - #4332

Merged
os-zhuang merged 2 commits into
mainfrom
claude/flow-executors-config-parsing-o66is4
Jul 31, 2026
Merged

feat(automation,spec): 流程执行器 parse() 自己的 config,未声明键在注册时报错 (#4277)#4332
os-zhuang merged 2 commits into
mainfrom
claude/flow-executors-config-parsing-o66is4

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #4277(#4045 步骤 3b —— 唯一被有意推迟的一项)。两半都装上了:

(a) 执行器 parse() 自己的 config —— 执行时强制

12 个带 Zod 契约的内置节点(CRUD 四件、screenmapnotifyhttploop/parallel/try_catch)现在在执行前把 node.config 过一遍各自的契约(新增 service-automation/builtin/parse-config.ts,结构化鉴权 safeParse —— service-automation 仍不直接依赖 zod,与 ledger 测试同一边界)。要点:

  • parse 失败 = guard 拒绝(errorClass: 'guard',fault 边不可路由)。config 是元数据,重跑不会变 —— 这正是 refuseNode 文档里"缺必填 config 键"的范例场景。guard-refusal-inventory 的 5 个必填键条目改为钉住契约拒绝的片段,分类不变。
  • 模板照旧合法:除 http 外都 parse 原始存储的 config(契约的类型槽都是 string / unknown,{token} 原样通过);http 因为执行器整体插值后才读,所以 parse 插值后的形状 —— 整串单 token 插值保留类型,timeoutMs: '{t}' 在契约看到之前已解析成数字(有测试钉住)。
  • 豁免:legacy 扁平 loop(无 config.body)先于 ADR-0031 构造存在,不 parse(注释 + 测试钉住)。
  • 顺手修正一处契约漏报:LoopConfigSchema.collection 从 string-only 放宽为 string | array 联合 —— 执行器一直接受内联数组(与 map.collection 共享解析逻辑,后者早已声明联合),string-only 是"照执行器写契约"时的漏报。run-history 的 big_loop 测试(内联数组)暴露了这一点。
  • try_catch 的 retry 默认值改由契约提供:声明了 retry 块但缺 retryDelayMs 时,基础延迟从执行器历史上的 0 变为契约文档化的 1000ms(changeset 有迁移说明)。

decision/script/wait/subflow/connector_action 保持无契约不 parse(wait/connector_action 的契约在 waitEventConfig/connectorConfig 兄弟块上,议题约束已核对);assignment 不可 parse,整体豁免。

(b) 未声明键 warn → error —— 注册时强制

registerFlow#4059 警告收紧为 hard throw:一次性列出所有违例(路径、声明键集、did-you-mean),并按 object.zod.tsUNKNOWN_KEY_GUIDANCE 模式加了 per-node-type 墓碑映射(FLOW_NODE_UNKNOWN_KEY_GUIDANCE),先立三条有据可查的:screen.visibleIfvisibleWhen(#3528)、create_record/update_record.fieldValuesfields(#2419/cloud#688)。豁免与 warn 时代一致:assignment 整体豁免、无 configSchema 类型天然跳过、keyValue map 处止步。所有 registerFlow 调用点(boot pull / kernel:ready / metadata:reloaded)已有 per-flow try/catch,坏 flow 是响亮跳过,不炸内核。

例子元数据修复(PD #12,在生产端修)

对三个示例 app 的全部 63 个 flow 跑了注册 + 执行两层审计,查出并修正:

  • showcase:showcase_inquiry_purge.reportshowcase_task_due_reminder.remind_owner 两个 notify 节点缺必填的 recipients/title —— 注意这两处今天运行期本来就必失败(执行器的 execute-time guard 早已要求),即 [P2] Provide a declarative time-relative trigger (avoid fragile date-equality on record-change) #1874 的提醒 demo 从未真正投递过;现在补上({record.assignee} / admin)。
  • app-todo:get_record.getAll 死键 ×2(换成声明机制 limit: 200,顺带让"取全部"意图第一次真实生效 —— 之前实际是 findOne 单条)、screen.message/buttons 死键(换成 description + waitForInput: true,按钮本就无任何读者)、select options 裸字符串改为契约的 { value, label } 对。

验证

  • service-automation 494/494,spec 7138/7138,根 pnpm test 132/132 任务全绿
  • 63 个示例 flow:0 注册拒绝、0 执行期类型违例(审计脚本双层扫描)
  • check:generated 全绿(仅 check:docs 曾陈旧,已 --fix 重新生成 4 个 MDX);api-surface / authorable-surface 无漂移
  • 新增 config-parse.test.ts(11 例:guard 不可路由、模板保留、http 插值后 parse、loop 双路径、enum/类型/必填拒绝);config-unknown-keys.test.ts 重写为钉住拒绝行为
  • changeset:.changeset/flow-executors-parse-config.md(spec minor + service-automation minor,含 FROM → TO 迁移说明)

objectui

无需改动:设计器表单读的是 descriptor 上手写的 configSchema 字面量(未变),json-schema-to-fields 的 keyValue 判定路径也未触及。该仓库不开 PR。

风险

议题已言明的既定风险:今天能注册/加载的存量 metadata,若带未声明键会在下次注册时被拒(错误信息自带处方),若声明键类型不符会在节点执行时被 guard 拒绝。示例仓内的全部此类存量已在本 PR 修净;错误文案对两类都给出改法(改名/删键,或在 descriptor configSchema 上声明)。

🤖 Generated with Claude Code

https://claude.ai/code/session_01CW8ZP3zUuC7ovSxqnN5o77


Generated by Claude Code

…d config keys reject at registration (#4277)

Half (a): the 12 contract-carrying builtins (CRUD quartet, screen, map,
notify, http, loop/parallel/try_catch) now parse node.config against their
Zod contracts before executing (builtin/parse-config.ts). A type or
missing-required violation refuses the node as a guard — not routable via
fault edges. Templates stay legal: string slots parse the raw config; http
parses post-interpolation, the shape its executor reads. A legacy
flat-graph loop (no config.body) stays exempt. LoopConfigSchema.collection
widened to string|array — the executor always accepted inline arrays
(map.collection already declared the union), so string-only under-declared.

Half (b): registerFlow now REJECTS config keys the node type's descriptor
configSchema does not declare (tightening the #4059 warning), naming the
path, the declared key set, a did-you-mean, and per-key tombstones
(the UNKNOWN_KEY_GUIDANCE pattern). assignment stays exempt wholesale (its
top-level keys are author variable names); schemaless types and keyValue
maps are unchanged.

Example metadata fixed at the producer (PD #12): two showcase notify nodes
missing the required recipients/title (both already failed at run time),
and app-todo's dead getAll/message/buttons keys plus bare-string select
options.

Closes #4277

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CW8ZP3zUuC7ovSxqnN5o77
@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 6:23am

Request Review

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

Copy link
Copy Markdown
Contributor

This PR is very large. Consider breaking it into smaller PRs for easier review.

@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/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.

…4277)

The docs-drift advisory on #4332 flagged flows.mdx, and it was right: the
node-property table and the strict-shells callout both described config as
an open record with no enforcement, which #4277 changed — undeclared keys
reject at registerFlow() and the contract-carrying builtins parse their
config at execute time.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CW8ZP3zUuC7ovSxqnN5o77
@os-zhuang
os-zhuang marked this pull request as ready for review July 31, 2026 06:40
@os-zhuang
os-zhuang merged commit b07d829 into main Jul 31, 2026
18 checks passed
@os-zhuang
os-zhuang deleted the claude/flow-executors-config-parsing-o66is4 branch July 31, 2026 06:40
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/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3b — wire the flow executors to parse() their config, and tighten the undeclared-key warning into an error

2 participants