Skip to content

[P3] Six flow-node config aliases bypass readAliasedConfig — no warning, no ledger, no retirement path #3796

Closed

Description

@os-zhuang

Found while auditing the alias debt named in AGENTS.md Prime Directive #12 (follow-on to #3713 / #3742 / #3754 / #3795).

Context — the shim exists and works

packages/services/service-automation/src/builtin/config-aliases.ts was built exactly for this problem. FlowNodeSchema.config is an unconstrained z.record(z.string(), z.unknown()), so the spec blesses no key — the executor is the only thing that gives config a shape at runtime. readAliasedConfig(cfg, nodeType, canonical, aliases, logger) gives that shape a declared contract: canonical wins, the alias still works for already-stored flows, and each alias warns once so authors get steered to the canonical key.

Its own doc block sets the retirement path, and it has been followed: filters → filter graduated out of the shim into the ADR-0087 D2 conversion layer (flow-node-crud-filter-alias, rewritten at load including the AutomationEngine.registerFlow rehydration seam), leaving object → objectName in crud-nodes.ts as the named next candidate.

The gap

Six alias fallbacks in the same directory never went through the shim — they are still open-coded ?? chains:

file line fallback
builtin/notify-node.ts 143 cfg.recipients ?? cfg.to
builtin/notify-node.ts 144 cfg.title ?? cfg.subject
builtin/notify-node.ts 145 cfg.message ?? cfg.body
builtin/notify-node.ts 149 cfg.actionUrl ?? cfg.url
builtin/screen-nodes.ts 166 cfg.function ?? cfg.functionName
builtin/screen-nodes.ts 229 cfg.inputs ?? cfg.input

Their precedence is fine — canonical is first in every one, so nothing is running the wrong branch today. That is not what makes them debt.

What they lack is everything the shim provides:

  • No warning. An author who writes to: instead of recipients: gets a flow that works forever and is never told they are on a deprecated key — the exact "fossilizes the wrong shape into a de-facto second contract and hides metadata-generation bugs" failure config-aliases.ts describes in its own header.
  • Nothing declares which key is canonical. With config unconstrained in the spec, the ?? order in the executor is the only statement of intent, and it is invisible to authoring tools, graph-lint, and skills.
  • No retirement path. The shim's ledger is what makes removal schedulable; these six can never "graduate" because nothing tracks them.
  • graph-lint cannot gate them for the same reason — there is no declared alias list to lint against.

Suggested fix

  1. Route all six through readAliasedConfig with the canonical key each ?? chain already implies, so they join the ledger and start warning.
  2. Add them to whatever list graph-lint consults, so newly published flows are rejected rather than silently accepted.
  3. Confirm the canonical choice per pair while doing it — inputs/input and function/functionName in particular deserve a deliberate answer rather than inheriting whatever the ?? happened to put first.
  4. Then treat them like object → objectName: candidates to graduate into the D2 conversion layer once the window elapses.

Low priority — nothing is broken at runtime. It is filed so the sweep's result is recorded rather than rediscovered, per PD #10.

Note for AGENTS.md

Prime Directive #12 currently reads:

The existing cfg.filter ?? cfg.filters / cfg.objectName ?? cfg.object in the flow executors are debt to pay down, not a pattern to copy.

Both halves have moved on since that was written: filters is retired into the conversion layer, and object → objectName is no longer a raw ?? — it sits behind readAliasedConfig with a warning and a documented graduation path. The sentence is worth refreshing so it points at the shim and the conversion layer as the established retirement mechanism (and at this issue for the stragglers), rather than at two examples that no longer look the way it describes.

Refs #3713, #3742, #3754, #3795, ADR-0087.

Activity

  1. added a commit that references this issue on Jul 28, 2026
  2. self-assigned this
    on Jul 29, 2026
  3. os-zhuang commented on Jul 30, 2026

    @os-zhuang
    ContributorAuthor

    已通过 #3976 关闭,落地方式与本 issue 原方案有一处升级:当前正处 protocol 17 大版本关口,跳过了"先进 readAliasedConfig shim 攒警告"的过渡阶段,七个别名(本 issue 的六个 + shim 里最后的 object→objectName)直接毕业进 ADR-0087 D2 转换层(flow-node-crud-object-alias / flow-node-notify-config-aliases / flow-node-script-config-aliases,live window,18 退役),shim 清空删除。

    两点勘误性发现记录在案:

    1. url/actionUrl 这对存在真实的双向契约矛盾——notify descriptor 的 configSchema 声明 url 为规范键,而执行器优先级、测试、示例全部以 actionUrl 为准。已定 actionUrl 为规范(下游 sys_notification.action_url、渠道契约、REST 读模型全链路同名;url 平台语义已被 "HTTP 端点" 占用),descriptor 修正。
    2. 本 issue 的 "Note for AGENTS.md" 在 [P3] Six flow-node config aliases bypass readAliasedConfig — no warning, no ledger, no retirement path #3796 提出时已由先行 changeset 处理过一轮;PD Add comprehensive test suite for Zod schema validation #12 现随 shim 删除再次更新为终局表述。

    发布门槛侧(本 issue 建议 2)由 cloud PR objectstack-ai/cloud#916 收口:graph-lint 新增 error 级 flow_config_deprecated_alias,覆盖七对别名及更早的 filters→filter。


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions