Repository navigation
feat(explain): add explanation report types and settings - #876
Anand Krishnamoorthi (anakrish) merged 1 commit into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Residual queries can produce incorrect authorization results for conflicting definitions and omit the rule-kind information needed to distinguish complete-rule values from partial-set results.
2 open issues
What changed in this PR
Adds the shared explanation and partial-evaluation contract behind the default-off explanations feature, preparing the RVM and language bindings for future capture and reporting without adding runtime behavior.
Changes:
- Defines report and residual types with explicit redaction and undefined-value serialization.
- Adds explanation settings and typed errors.
- Adds reusable fixtures and tests for serialization, defaults, enum names, and error messages.
| File | Description |
|---|---|
src/explain/tests.rs |
Tests serialization and public contract behavior. |
src/explain/settings.rs |
Defines value, condition, and reporting-scope settings. |
src/explain/report.rs |
Defines explanation reports, residuals, and serialization. |
src/explain/mod.rs |
Exports the API and documents compatibility rules. |
src/explain/fixtures.rs |
Provides report and partial-evaluation fixtures. |
src/explain/error.rs |
Defines explanation errors and wraps VM errors. |
Review effort: Balanced
Add the typed contract shared by explanation capture, reporting and
partial evaluation, behind the default-off `explanations` feature. It has
no runtime behaviour yet; producers and consumers land separately.
- `ExplanationReport` with per-evaluation `RuleExplanation`s, their
emissions, evaluated definition bodies, conditions, operands and loop
witnesses.
- `PartialEvalResult` with `ResidualQuery` alternatives (with a `key`
for partial objects) and structured `ResidualCondition`s. Its `kind`
(`RuleKind`) says how the alternatives combine and how an empty outcome
reads: undefined, an empty set or an empty object. Its mandatory
`status` (`ResidualStatus`) is `exact` only when the alternatives
describe the outcome; independent definitions that might conflict, or
residuals JSON can't express, give `possible_conflict` or
`unsupported` with no alternatives. Residual consumers fail closed on
any other status and on an unknown kind.
- `ReportedValue { Known, Redacted }` so a withheld runtime value is not
representable as a placeholder. Redacted values serialize as a
`redacted`/`*_redacted: true` flag with no value.
- `ExplanationSettings` (`ValueMode`, `ConditionMode`,
`ExplanationScope`) and the `ExplainError` enum.
- All public types are `#[non_exhaustive]` and serialize with snake_case
names; undefined values are omitted so they stay distinct from `false`,
`null` and the string "<undefined>".
- `cfg(test)` fixtures and exact-JSON tests for downstream unit tests.
Reports are complete by construction; a truncation signal will be added
with bounded tracing.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
365939b to
982225d
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The in exception permits nested values whose JSON serialization changes Rego semantics, and the reporting contract incorrectly describes defined empty partial-rule collections as undefined.
2 open issues
2 resolved since last review
Review effort: Balanced
| /// Final value of the rule. When no definition succeeded, this is the | ||
| /// rule's default value, if it has one. It is undefined when no | ||
| /// definition succeeded and there is no default, or when complete | ||
| /// definitions conflict (the default is not used then). Omitted from | ||
| /// JSON when undefined. |
| /// JSON can't distinguish a set from an array, or a non-string object key | ||
| /// from a string, so producers return [`ResidualStatus::Unsupported`] when a | ||
| /// residual key or value, or a condition value other than the collection of | ||
| /// an `in` test, contains a set or an object with non-string keys. |
Anand Krishnamoorthi (anakrish)
left a comment
There was a problem hiding this comment.
LGTM
snehabandla The remaining issues can be addressed in next PR.
64bb060
into
microsoft:main


Summary
This PR adds the typed contract shared by explanation capture, partial evaluation and the bindings. It is behind the default-off
explanationsfeature and has no runtime behaviour yet: it defines types, settings, errors, serialization, and fixtures for downstream unit tests.ExplanationReport):RuleExplanationper rule evaluation.key) and evaluated definition bodies (index+bodyforelsebranches and query blocks).success/failure/undefined/assumed), operator, operands andLoopWitness(saturating counters, outcome-matching sample).PartialEvalResult):ResidualQuery { value, conditions }alternatives. Complete rules and functions combine them as ordered first-match; partial sets combine them as a union.ResidualConditionis normalized toinput_path op value, withResidualKind::Truthyfornot input.x.ReportedValue { Known(Value), Redacted }):redacted: true(operands) orresult_redacted/key_redacted/value_redacted: true(dependency rules and emissions).query_resultand the queried rule's top-level result are never redacted, because the caller receives them anyway.ExplanationSettings { value_mode, condition_mode, scope }, defaulting to Redacted / PrimaryOnly / All.ExplainError(NotEnabled,EmissionIndexOutOfRange,Vm(VmError)).Compatibility
#[non_exhaustive], and enums serialize in snake_case (Operatoras Rego syntax).false,nulland"<undefined>".Valueserialization).Testing
All runs used Rust 1.95.0.
--features explanations):falsevsnullvs"<undefined>"in 9 value positions.ReportedValue.From<RuleType>, default settings, and error messages.cargo fmt --checkpasses.cargo clippy --all-features --all-targets -D warningspasses.cargo clippy --no-default-features -- -D warningspasses. The separate--all-targetsrun has pre-existing bench failures unchanged frommain.cargo docgives 0 warnings insrc/explain.