cel.Engine.Compile type-checks an expression against the declared variables but only returns an opaque *CompiledExpression — there's no way for a caller to ask "does this expression statically return bool?"
This matters for callers who want config validated at admission/reconcile time rather than discovering a wrong-type expression on the first live evaluation. Concretely: ToolHive's operator lets admins author a CEL expression (actorMatcher) that must evaluate to bool. Something like "engineering" or 1 type-checks fine (any expression over the declared variables compiles), so it passes config validation, gets reported as valid, and then fails on every runtime evaluation via EvaluateBool's runtime type assertion — a config mistake that could have been caught once at reconcile time instead surfaces as an operational failure repeatedly.
cel-go's own cel.Ast.OutputType() already exposes this from the checked AST — cel.Engine.Compile just doesn't surface it. Proposed API, e.g.:
func (ce *CompiledExpression) OutputType() *cel.Type
or a narrower Engine.CompileBool(expr string) (*CompiledExpression, error) that rejects non-bool statically in one call.
Reference: stacklok/toolhive#6364 (review comment) — code currently documents this gap in compileActorMatcher's doc comment rather than working around it.
cel.Engine.Compiletype-checks an expression against the declared variables but only returns an opaque*CompiledExpression— there's no way for a caller to ask "does this expression statically return bool?"This matters for callers who want config validated at admission/reconcile time rather than discovering a wrong-type expression on the first live evaluation. Concretely: ToolHive's operator lets admins author a CEL expression (
actorMatcher) that must evaluate to bool. Something like"engineering"or1type-checks fine (any expression over the declared variables compiles), so it passes config validation, gets reported as valid, and then fails on every runtime evaluation viaEvaluateBool's runtime type assertion — a config mistake that could have been caught once at reconcile time instead surfaces as an operational failure repeatedly.cel-go's owncel.Ast.OutputType()already exposes this from the checked AST —cel.Engine.Compilejust doesn't surface it. Proposed API, e.g.:or a narrower
Engine.CompileBool(expr string) (*CompiledExpression, error)that rejects non-bool statically in one call.Reference: stacklok/toolhive#6364 (review comment) — code currently documents this gap in
compileActorMatcher's doc comment rather than working around it.