Repository: cedar-policy/cedar
Pull request #1568 (2025-04-11 21:15:57)

Stablize Level Checking
## Description of changes

Stabilizes level checking, removing the `level-validate` feature.

I don't plan to merge this until Monday since I don't want to try to make the corresponding updates to `cedar-spec` this evening.

## Issue #, if available

## Checklist for requesting a review

The change in this PR is (choose one, and delete the other options):

- [ ] A breaking change requiring a major version bump to `cedar-policy` (e.g., changes to the signature of an existing API).
- [x] A backwards-compatible change requiring a minor version bump to `cedar-policy` (e.g., addition of a new API).
- [ ] A bug fix or other functionality change requiring a patch to `cedar-policy`.
- [ ] A change "invisible" to users (e.g., documentation, changes to "internal" crates like `cedar-policy-core`, `cedar-validator`, etc.)
- [ ] A change (breaking or otherwise) that only impacts unreleased or experimental code.

I confirm that this PR (choose one, and delete the other options):

- [x] Updates the "Unreleased" section of the CHANGELOG with a description of my change (required for major/minor version bumps).
- [ ] Does not update the CHANGELOG because my change does not significantly impact released code.

I confirm that [`cedar-spec`](https://github.com/cedar-policy/cedar-spec) (choose one, and delete the other options):

- [ ] Does not require updates because my change does not impact the Cedar formal model or DRT infrastructure.
- [x] Requires updates, and I have made / will make these updates myself. (Please include in your description a timeline or link to the relevant PR in `cedar-spec`, and how you have tested that your updates are correct.)
- [ ] Requires updates, but I do not plan to make them in the near future. (Make sure that your changes are hidden behind a feature flag to mark them as experimental.)
- [ ] I'm not sure how my change impacts `cedar-spec`. (Post your PR anyways, and we'll discuss in the comments.)

I confirm that [`docs.cedarpolicy.com`](https://docs.cedarpolicy.com/) (choose one, and delete the other options):

- [ ] Does not require updates because my change does not impact the Cedar language specification.
- [x] Requires updates, and I have made / will make these updates myself. (Please include in your description a timeline or link to the relevant PR in [`cedar-docs`](https://github.com/cedar-policy/cedar-docs). PRs should be targeted at a `staging-X.Y` branch, not `main`.)
- [ ] I'm not sure how my change impacts the documentation. (Post your PR anyways, and we'll discuss in the comments.)


github-actions[bot]: 
# Coverage Report
Head Commit: ```ee82b4476b0f6d9fd7368fdb125f3a1eddd5bfa8```

Base Commit: ```992ab85440ce3bfaf9ed9be66efc29ef6c400a9d```

[Download the full coverage report.](https://github.com/cedar-policy/cedar/actions/runs/14412174926/artifacts/2930701166)

## Coverage of Added or Modified Lines of Rust Code
**Required coverage:** 80.00%

**Actual coverage:** 100.00%

**Status:** PASSED :white_check_mark:

<details>
<summary><b>Details</b></summary>

| File | Status | Covered | Coverage | Missed Lines |
|:-----|:------:|:-------:|---------:|:-------------|

</details>

## Coverage of All Lines of Rust Code
**Required coverage:** 80.00%

**Actual coverage:** 81.62%

**Status:** PASSED :white_check_mark:

<details>
<summary><b>Details</b></summary>

| Package | Status | Covered | Coverage | Base Coverage |
|:--------|:------:|:-------:|---------:|--------------:|
cedar-policy | :red_circle: | 3075/4473 | 68.75% | --
cedar-policy-cli | :red_circle: | 571/972 | 58.74% | --
cedar-policy-core | :green_circle: | 11716/13917 | 84.18% | --
cedar-policy-formatter | :green_circle: | 914/1043 | 87.63% | --
cedar-policy-validator | :green_circle: | 8540/9972 | 85.64% | --
cedar-wasm | :red_circle: | 0/29 | 0.00% | --
</details>


Patch:
diff --git a/cedar-policy-cli/CHANGELOG.md b/cedar-policy-cli/CHANGELOG.md
index fa17cd34..b53cfc9c 100644
--- a/cedar-policy-cli/CHANGELOG.md
+++ b/cedar-policy-cli/CHANGELOG.md
@@ -8,8 +8,8 @@ Changes to the Cedar language, which are likely to affect users of the CLI, are
 ### Added
 
 - Added `json-to-cedar` direction to `translate-policy` command. (#1510, resolving #461)
-- Added `--level` option to the `validate` command, exposing the `level-validate`
-  experimental feature through the CLI. (#1508, resolving #1501)
+- Added `--level` option to the `validate` command, exposing level validation
+  through the CLI. (#1508, resolving #1501)
 - Improved the `check-parse` command, which now checks the parse of policies, schema,
   and/or entities (whatever is passed). (#1548)
 
diff --git a/cedar-policy-cli/Cargo.toml b/cedar-policy-cli/Cargo.toml
index caa31541..e1142ffc 100644
--- a/cedar-policy-cli/Cargo.toml
+++ b/cedar-policy-cli/Cargo.toml
@@ -22,11 +22,10 @@ semver = "1.0.26"
 
 [features]
 default = []
-experimental = ["level-validate", "permissive-validate", "partial-validate", "partial-eval"]
+experimental = ["permissive-validate", "partial-validate", "partial-eval"]
 permissive-validate = ["cedar-policy/permissive-validate"]
 partial-validate = ["cedar-policy/partial-validate"]
 partial-eval = ["cedar-policy/partial-eval"]
-level-validate = ["cedar-policy/level-validate"]
 
 [dev-dependencies]
 assert_cmd = "2.0"
diff --git a/cedar-policy-cli/src/lib.rs b/cedar-policy-cli/src/lib.rs
index 8f37da8a..0ba197db 100644
--- a/cedar-policy-cli/src/lib.rs
+++ b/cedar-policy-cli/src/lib.rs
@@ -190,8 +190,6 @@ pub struct ValidateArgs {
     #[arg(long, value_enum, default_value_t = ValidationMode::Strict)]
     pub validation_mode: ValidationMode,
     /// Validate the policy at this level.
-    /// This option is experimental and will cause the CLI to exit if it was not
-    /// built with the experimental feature `level-validate` enabled.
     #[arg(long)]
     pub level: Option<u32>,
 }
@@ -894,14 +892,7 @@ pub fn validate(args: &ValidateArgs) -> CedarExitCode {
 
     let validator = Validator::new(schema);
 
-    #[cfg_attr(not(feature = "level-validate"), allow(unused_variables))]
     let result = if let Some(level) = args.level {
-        #[cfg(not(feature = "level-validate"))]
-        {
-            eprintln!("Error: arguments include the experimental option `--level`, but this executable was not built with `level-validate` experimental feature enabled");
-            return CedarExitCode::Failure;
-        }
-        #[cfg(feature = "level-validate")]
         validator.validate_with_level(&pset, mode, level)
     } else {
         validator.validate(&pset, mode)
diff --git a/cedar-policy-validator/Cargo.toml b/cedar-policy-validator/Cargo.toml
index 1377e103..9d912f5d 100644
--- a/cedar-policy-validator/Cargo.toml
+++ b/cedar-policy-validator/Cargo.toml
@@ -47,7 +47,6 @@ arbitrary = ["dep:arbitrary", "cedar-policy-core/arbitrary"]
 
 # Experimental features.
 partial-validate = []
-level-validate = []
 wasm = ["serde-wasm-bindgen", "tsify", "wasm-bindgen"]
 entity-manifest = []
 tolerant-ast = ["cedar-policy-core/tolerant-ast"]
diff --git a/cedar-policy/CHANGELOG.md b/cedar-policy/CHANGELOG.md
index 07ee4b80..02b16b6c 100644
--- a/cedar-policy/CHANGELOG.md
+++ b/cedar-policy/CHANGELOG.md
@@ -29,6 +29,11 @@ Cedar Language Version: TBD
 
 ### Added
 
+- Stabilized [RFC 76](https://github.com/cedar-policy/rfcs/pull/76), removing
+  the experimental `level-validate` feature flag. That functionality is now available
+  without the feature flag. Stabilization comes with changes to fix bugs in the features.
+  Level validation is now more permissive when checking `if` expressions (fixing #1507),
+  and stricter when checking record literals and entity tag operations (fixing #1505 and #1503). (#1567)
 - Added `Entities::remove_entities()` to remove `Entity`s from an `Entities` struct (resolving #701)
 - Added `PolicySet::merge()` to merge a `PolicySet` into another `PolicySet` struct (resolving #610)
 - Implemented [RFC 53 (enumerated entity types)](https://github.com/cedar-policy/rfcs/blob/main/text/0053-enum-entities.md)  (#1377)
@@ -47,12 +52,6 @@ Cedar Language Version: TBD
 - Added `PartialResponse::unknown_entities` method (#1557)
 - Added `Entities::len` and `Entities::is_empty` methods (#1562, resolving #1523)
 
-### Fixed
-
-- Fixed bugs in experimental `level-validate` feature. Level validation is now
-  more permissive when checking `if` expressions (fixing #1507), and stricter when
-  checking record literals and entity tag operations (fixing #1505 and #1503). (#1567)
-
 ## [4.3.3] - 2025-02-25
 
 ### Changed
diff --git a/cedar-policy/Cargo.toml b/cedar-policy/Cargo.toml
index bc871f41..cf026051 100644
--- a/cedar-policy/Cargo.toml
+++ b/cedar-policy/Cargo.toml
@@ -52,13 +52,12 @@ corpus-timing = []
 
 # Experimental features.
 # Enable all experimental features with `cargo build --features "experimental"`
-experimental = ["partial-eval", "permissive-validate", "partial-validate", "level-validate", "entity-manifest", "protobufs", "tolerant-ast", "extended-schema"]
+experimental = ["partial-eval", "permissive-validate", "partial-validate", "entity-manifest", "protobufs", "tolerant-ast", "extended-schema"]
 entity-manifest = ["cedar-policy-validator/entity-manifest"]
 partial-eval = ["cedar-policy-core/partial-eval", "cedar-policy-validator/partial-eval"]
 permissive-validate = []
 partial-validate = ["cedar-policy-validator/partial-validate"]
 protobufs = ["dep:prost", "dep:prost-build"]
-level-validate = ["cedar-policy-validator/level-validate"]
 wasm = ["serde-wasm-bindgen", "tsify", "wasm-bindgen"]
 tolerant-ast = ["cedar-policy-core/tolerant-ast",  "cedar-policy-validator/tolerant-ast", "cedar-policy-formatter/tolerant-ast"]
 extended-schema = ["cedar-policy-validator/extended-schema"]
diff --git a/cedar-policy/src/api.rs b/cedar-policy/src/api.rs
index d847b1eb..f2349d61 100644
--- a/cedar-policy/src/api.rs
+++ b/cedar-policy/src/api.rs
@@ -1385,7 +1385,6 @@ impl Validator {
         ValidationResult::from(self.0.validate(&pset.ast, mode.into()))
     }
 
-    #[cfg(feature = "level-validate")]
     /// Validate all policies in a policy set, collecting all validation errors
     /// found into the returned `ValidationResult`. If validation passes, run level
     /// validation (RFC 76). Each error is returned together with the policy id of the policy


Test patch:
diff --git a/cedar-policy-cli/tests/sample.rs b/cedar-policy-cli/tests/sample.rs
index 84718e04..539e84bd 100644
--- a/cedar-policy-cli/tests/sample.rs
+++ b/cedar-policy-cli/tests/sample.rs
@@ -630,7 +630,6 @@ fn test_validate_samples(
     assert_eq!(exit_code, output, "{:#?}", cmd)
 }
 
-#[cfg(feature = "level-validate")]
 #[rstest]
 #[case(
     "sample-data/tiny_sandboxes/level-validation/policy-level-0.cedar",
diff --git a/cedar-policy/src/test/test.rs b/cedar-policy/src/test/test.rs
index ae653551..344043da 100644
--- a/cedar-policy/src/test/test.rs
+++ b/cedar-policy/src/test/test.rs
@@ -4206,7 +4206,6 @@ mod partial_schema {
     }
 }
 
-#[cfg(feature = "level-validate")]
 mod level_validation_tests {
     use crate::ValidationMode;
     use crate::{Policy, PolicySet, ValidationError, Validator};
diff --git a/cedar-testing/Cargo.toml b/cedar-testing/Cargo.toml
index 3e9e9157..41cb6790 100644
--- a/cedar-testing/Cargo.toml
+++ b/cedar-testing/Cargo.toml
@@ -18,7 +18,6 @@ miette = { version = "7.5.0", features = ["fancy"] }
 default = ["ipaddr", "decimal"]
 decimal = ["cedar-policy/decimal"]
 ipaddr = ["cedar-policy/ipaddr"]
-level-validate = ["cedar-policy/level-validate"]
 integration-testing = []
 entity-manifest = ["cedar-policy/entity-manifest"]
 
diff --git a/cedar-testing/src/cedar_test_impl.rs b/cedar-testing/src/cedar_test_impl.rs
index 27004a07..e6bac1c9 100644
--- a/cedar-testing/src/cedar_test_impl.rs
+++ b/cedar-testing/src/cedar_test_impl.rs
@@ -174,7 +174,6 @@ pub trait CedarTestImplementation {
         mode: ValidationMode,
     ) -> TestResult<TestValidationResult>;
 
-    #[cfg(feature = "level-validate")]
     /// Custom validator entry point with level.
     fn validate_with_level(
         &self,
@@ -333,7 +332,6 @@ impl CedarTestImplementation for RustEngine {
         TestResult::Success(response)
     }
 
-    #[cfg(feature = "level-validate")]
     fn validate_with_level(
         &self,
         schema: &ValidatorSchema,