Explain mode

Explain mode traces every condition in every rule, showing which checks matched, which didn't, and what values were resolved. Great for debugging and audit trails.

Enabling explain mode

CLI:

cc evaluate --policy ./policies --input ./input.json --explain

Go API:

eng := evaluator.NewFromPolicies(policies,
    evaluator.WithExplain(true),
)
results := eng.Evaluate(doc)
fmt.Print(evaluator.FormatExplain(results))

Trace output

Each condition is annotated with + (matched), - (did not match), and the actual value resolved from the document.

RULE "blast-radius" [forbid] -> DENIED
  + condition: count(plan.destroys) > 5 -> true (got 8)
  - unless: author.teams contains "platform-team" -> false (got [dev, backend])
  -> too many destroys (8) — requires platform-team

RULE "sensitive-resources" [forbid] -> PASSED
  + condition: resource.type in ["aws_iam_role", ...] -> true (got "aws_iam_role")
  + unless: author.teams contains "enterprise-security" -> true (got [enterprise-security, dev])
  -> saved by unless clause

RULE "no-draft-production" [forbid] -> PASSED
  + condition: pr.draft == true -> true (got true)
  - condition: project.workspace == "production" -> false (got "staging")
  -> conditions not met, rule did not fire

Complete traces

In explain mode, the evaluator continues evaluating all conditions and unless clauses even after a mismatch, so the trace is always complete. This is slower than normal evaluation but gives you the full picture for debugging.

Use cases

  • Debugging a confusing rule result — see exactly which condition failed and what value it saw.
  • Audit trails — log per-condition traces as structured output for compliance.
  • Dry-run review — explain mode on a set of sample inputs is an effective way to validate a new policy before enforcing it.