audit support of evaluate or program.execute method(Support for execution tracing and variable-level audit logs) #30
Description
Currently, the common-expression-language library evaluates expressions but provides limited or no visibility into the evaluation process. Specifically, there is:
No built-in execution trace.
No automatic "why it failed" explanation for complex logic.
No variable-level audit log to identify which specific part of the context triggered a result.
When dealing with collection-based macros like .exists(), etc,. it is difficult to debug which specific element in a list caused the expression to return True or False.
I would like a mechanism that returns a trace or an audit log. This trace should identify which value in the context resulted in the final pass, fail, or result.
Additional context (Example)
Consider the following scenario:
Expression: value.exists(v, v.x == 5)
Context: {"value": [{"x": 7}, {"x": 15}, {"x": 5}]}
The Issue:
The library correctly returns True because the third element matches the condition. However, there is no built-in way to programmatically identify that {"x": 5} was the specific element that satisfied the condition. In more complex real-world scenarios, knowing "why" or "which" value triggered the result is critical for auditing and debugging.
Suggested Output/API:
It would be helpful if the library could provide an output similar to:
{
"result": true,
"trace": {
"matched_element": {"x": 5},
"index": 2,
"expression_part": "v.x == 5"
}
}
Thanks in advance, please let me know if any more information or details needed.
Activity
hardbyte commentedon Sep 14, 2026
Thanks for the detailed write-up. Triage notes so the status is clear:
Why this isn't available yet. The evaluator in the upstream
celcrate is a plain recursiveValue::resolve(expr, ctx)walk with no observer or hook points, so this wrapper has nothing to record. cel-go gets this from itsEvalState/OptTrackStatemachinery, which tracks the value produced at each AST node; cel-rust has no equivalent today. A real trace therefore has to land in cel-rust first (the closest existing thread there is cel-rust/cel-rust#314 about exposing source info on the success path). I'll keep this open as the tracking issue for exposing it once it exists.What works today. Ask the expression to return the evidence alongside the verdict. Comprehension macros compose, and a CEL map literal lets one evaluation return both:
For
&&/||chains, splitting the policy into named sub-expressions at the application level and evaluating each one gives you a "which clause failed" report with the tools that exist now.Possible intermediate step in this wrapper. Exposing the parsed AST from
Programwould let a Python helper walk sub-expressions and re-evaluate them to explain a result without waiting on upstream, at the cost of re-evaluation (and re-running any custom functions). I've opened a separate issue for the AST exposure; if that lands, anexplain()helper built on it could cover theexists/allcase you describe.Generated by Claude Code