Skip to content

fix(formula): catch unknown functions in CEL conditions at build (#1877)#1896

Merged
os-zhuang merged 2 commits into
mainfrom
fix/1877-unknown-condition-functions
Jun 15, 2026
Merged

fix(formula): catch unknown functions in CEL conditions at build (#1877)#1896
os-zhuang merged 2 commits into
mainfrom
fix/1877-unknown-condition-functions

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #1877.

Problem

A flow whose trigger/edge condition calls an unknown functionPRIOR(status), a typo'd isBlnk(...) — was silently accepted: build + typecheck pass, then the broken condition no-op'd the flow at runtime with no log line. The reporter hit this on @objectstack 9.5.1 while runtime-testing the objectstack-ai/templates set (signal_to_topic_promotion, cta_creation_default).

Root cause

In packages/formula/src/cel-engine.ts, compile() discarded the type-check verdict:

const checkErrors = compiled.check?.();
if (checkErrors && Array.isArray(checkErrors) && checkErrors.length > 0) { ... }

cel-js's check() returns a TypeCheckResult object ({ valid, type?, error? }), never an array — so Array.isArray(...) was always false and the verdict was dropped. An unknown function type-checks as found no matching overload for 'PRIOR(dyn)', but that result never surfaced. Every author surface (objectstack compile via validate-expressions.ts, registerFlow's validateFlowExpressions, and the agent validate_expression tool) routes through this one compile(), so all of them accepted the predicate.

Fix

Read the documented { valid, error } shape. One change closes the gap for flow conditions, validation rules, and field formulas at once.

The runtime half of the issue is already loud on current main (post-ADR-0032): the unknown-function fault message found no matching overload doesn't match the no such overload numeric-retry guard, so it propagates as an attributed flow failure rather than return false. The reporter's silent skip was on 9.5.1, which predates that work — but build validation stayed broken until this change.

Verification

  • Confirmed empirically (against the real registered stdlib) that cel-js's check() cleanly separates valid predicates (record.rating >= 4, !isBlank(...), previous.status != null, every existing example flow condition) from unknown-function ones — no false positives.
  • New regression tests in cel-engine.test.ts and validate.test.ts (PRIOR(), short-circuit-guarded unknown call, typo'd isBlnk, plus assertions that registered stdlib isBlank still passes).
  • @objectstack/formula 95 passed · @objectstack/service-automation 187 passed · tsc --noEmit clean.

🤖 Generated with Claude Code

cel-js's `check()` returns a `TypeCheckResult` object (`{ valid, error }`),
not an array, so `compile()`'s `Array.isArray(checkErrors)` guard never
matched and the type-check verdict was silently discarded. A condition
calling an unknown function (`PRIOR(status)`, a typo'd `isBlnk(...)`)
type-checks as `found no matching overload`, but that result never
surfaced — so `objectstack compile`, `registerFlow`, and the
`validate_expression` tool all accepted the predicate, which then
silently no-op'd the flow at runtime.

Read the documented `{ valid, error }` shape instead. Because every
author surface routes through this one `compile()`, the fix closes the
gap for flow conditions, validation rules, and field formulas at once.
Verified cel-js's `check()` cleanly separates valid predicates from
unknown-function ones with no false positives on existing conditions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 15, 2026 7:22am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling labels Jun 15, 2026
@os-zhuang
os-zhuang merged commit bb00a50 into main Jun 15, 2026
13 checks passed
@os-zhuang
os-zhuang deleted the fix/1877-unknown-condition-functions branch June 15, 2026 07:27
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/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P1] Flow trigger conditions with unknown functions are silently skipped (no error, passes build)

1 participant