Skip to content

ActionParam.validation / ActionParamDef.validation is declared, never read, and rejected by the spec parser #3201

Description

@os-zhuang

Found while fixing objectui#3174 (the referenceTo vs reference spelling split). Recording only — out of scope there, because retiring it is its own decision with its own blast radius.

The gap

validation?: string is declared on both halves of the action-param contract:

  • authoring — @object-ui/types' ActionParam (packages/types/src/ui-action.ts)
  • resolved — @object-ui/core's ActionParamDef (packages/core/src/actions/ActionRunner.ts)

Nothing reads it, on either side:

  • resolveActionParams() (packages/app-shell/src/utils/resolveActionParams.ts) never copies it onto the resolved param — it is not in RawActionParam at all.
  • paramToField() (packages/app-shell/src/utils/paramToField.ts) never maps it into the field object the widgets consume.
  • buildValidationRules() in @object-ui/fields builds rules from required / minLength / maxLength / pattern / type field metadata. There is no validation branch, so even if it reached the field it would be inert.

And it is not authorable either: ActionParamSchema in @objectstack/spec/ui is .strict() with a custom unknown-key error, and validation is not in its ACTION_PARAM_KEYS. So an authored validation: '...' is a hard parse rejection on the server, while tsc against the public type accepts it.

That is exactly the objectui#3174 shape — declared, inert, and blessed by a type the platform's own parser refuses — minus the second spelling that made #3174 worse.

Why it was left standing in #3174

#3174's fix removed the nine resolved-side picker keys (referenceTo, displayField, idField, descriptionField, titleFormat, lookupColumns, lookupFilters, lookupPageSize, dependsOn) from the authoring ActionParam, and installed a drift guard pinning the rule "the authoring type declares exactly the spec's authorable keys". validation is carried as the one named exception in that guard, so a second undeclared local key cannot appear without a decision — but the exception itself is debt, and this issue is the ledger entry for it.

The decision this needs

  1. Remove it from ActionParam (and probably ActionParamDef too), on the enforce-or-remove reading: it can never work as authored, so declaring it can only mislead. Breaking change to two published types; needs a changeset.
  2. Implement it — decide what an "expression" means here (CEL? a formula? a regex?), add it to ActionParamSchema in @objectstack/spec (cross-repo contract change), have the resolver carry it and paramToField lower it into a real validation rule.

Option 1 is the ADR-0049 default and the cheaper honest answer; option 2 is only worth it if per-param validation is a capability we actually want, in which case the spec is where it starts. Either way the gate is a test asserting that whatever the authoring type declares, the param dialog enforces.

Acceptance

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions