Summary
validateExpression (@objectstack/formula) reports ok: true, zero errors and zero warnings for a CEL source that calls a function the engine does not have. The same source then fails at runtime with found no matching overload. Authoring-time validation — the publish gate — does not check that the functions a predicate calls exist.
Measured
@objectstack/formula@17.2.0 (@marcbachmann/cel-js@8.0.0), node, one process:
"totallyBogusFn(1,2)" validate.ok= true warnings= 0 | runtime.ok= false - found no matching overload for 'totallyBogusFn(int, int)'
"record.x.nosuchmethod('a')" validate.ok= true warnings= 0 | runtime.ok= false - found no matching overload for 'dyn.nosuchmethod(string)'
"upper('a')" validate.ok= true warnings= 0 | runtime.ok= true
Both the global-call form and the receiver/member-call form are affected. validateExpression catches parse errors and unbound roots today (SCOPE_ROOTS is enforced), so the function name is the one part of a call that nothing checks.
Why it matters
The runtime fault is not loud where it lands. On the ObjectUI action surfaces a faulting visible predicate is fail-CLOSED — ActionEngine.getActionsForLocation and DeclaredActionsBar both evaluate with throwOnError: true and hide the action on a throw. So a single mistyped or non-existent function name in an authored predicate produces:
- publish: clean, no error, no warning;
- runtime: the action is invisible for every user, including ones who hold the grant;
- the only signal is one deduped
console.warn in the browser.
That is the failure mode metadata validation exists to prevent, and it is the one an AI-authored app is most likely to hit — a plausible-looking function name that does not exist is exactly what a generator invents. Measured on this while investigating objectui#4421, where an authored current_user.can(object, verb) predicate validated clean and then faulted at runtime on every surface.
Suggested direction (not a decision)
The engine already knows its own function registry — CEL_STDLIB_FUNCTIONS is exported, and cel-js Environment exposes getDefinitions(). Resolving each call node in the parsed AST against that set inside validateExpression would turn this into a publish-time error with a nearestName suggestion (the helper already exists in this package for identifiers). Whether an unknown function should be an ERROR or a WARNING is a contract call — an error is the enforce-or-remove-consistent answer, but it can reject predicates that a host registers extra functions for, if any host does that.
Filed unassigned from an objectui dev seat; no fix attempted here.
Generated by Claude Code
Summary
validateExpression(@objectstack/formula) reportsok: true, zero errors and zero warnings for a CEL source that calls a function the engine does not have. The same source then fails at runtime withfound no matching overload. Authoring-time validation — the publish gate — does not check that the functions a predicate calls exist.Measured
@objectstack/formula@17.2.0(@marcbachmann/cel-js@8.0.0), node, one process:Both the global-call form and the receiver/member-call form are affected.
validateExpressioncatches parse errors and unbound roots today (SCOPE_ROOTS is enforced), so the function name is the one part of a call that nothing checks.Why it matters
The runtime fault is not loud where it lands. On the ObjectUI action surfaces a faulting
visiblepredicate is fail-CLOSED —ActionEngine.getActionsForLocationandDeclaredActionsBarboth evaluate withthrowOnError: trueand hide the action on a throw. So a single mistyped or non-existent function name in an authored predicate produces:console.warnin the browser.That is the failure mode metadata validation exists to prevent, and it is the one an AI-authored app is most likely to hit — a plausible-looking function name that does not exist is exactly what a generator invents. Measured on this while investigating objectui#4421, where an authored
current_user.can(object, verb)predicate validated clean and then faulted at runtime on every surface.Suggested direction (not a decision)
The engine already knows its own function registry —
CEL_STDLIB_FUNCTIONSis exported, andcel-jsEnvironmentexposesgetDefinitions(). Resolving each call node in the parsed AST against that set insidevalidateExpressionwould turn this into a publish-time error with anearestNamesuggestion (the helper already exists in this package for identifiers). Whether an unknown function should be an ERROR or a WARNING is a contract call — an error is the enforce-or-remove-consistent answer, but it can reject predicates that a host registers extra functions for, if any host does that.Filed unassigned from an objectui dev seat; no fix attempted here.
Generated by Claude Code