Skip to content

lint: validate-visibility-predicates passes unknown CEL functions clean (the validateExpression premise is falsified) — ruled: extend to function-existence ERROR, scoped supersession of the parse-only ruling #13594

Description

@claude

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions