Skip to content

audit support of evaluate or program.execute method(Support for execution tracing and variable-level audit logs) #30

Open

Description

@vkumarkn

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

  1. hardbyte commented on Sep 14, 2026

    @hardbyte
    Owner

    Thanks for the detailed write-up. Triage notes so the status is clear:

    Why this isn't available yet. The evaluator in the upstream cel crate is a plain recursive Value::resolve(expr, ctx) walk with no observer or hook points, so this wrapper has nothing to record. cel-go gets this from its EvalState / OptTrackState machinery, 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:

    import cel
    
    ctx = {"value": [{"x": 7}, {"x": 15}, {"x": 5}]}
    cel.evaluate(
        '{"result": value.exists(v, v.x == 5), "matched": value.filter(v, v.x == 5)}',
        ctx,
    )
    # {'result': True, 'matched': [{'x': 5}]}
    
    # Per-index verdicts, if you need positions rather than elements:
    cel.evaluate("value.map(v, v.x == 5)", ctx)  # [False, False, True]

    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 Program would 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, an explain() helper built on it could cover the exists/all case you describe.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions