From 4ee938aec84433e6c098f7d6643d0a16cf5a78f1 Mon Sep 17 00:00:00 2001 From: Sean Huh Date: Fri, 7 Aug 2026 16:31:07 -0700 Subject: [PATCH] Consolidate emit to output for aggregate policies PiperOrigin-RevId: 961178627 --- README.md | 26 +++++++++---------- conformance/testdata/aggregate/policy.yaml | 4 +-- .../policy.yaml | 2 +- .../policy.yaml | 2 +- .../aggregate_shadowed_variables/policy.yaml | 2 +- .../aggregate_false_condition/policy.yaml | 2 +- .../policy.yaml | 6 ++--- .../policy.yaml | 2 +- .../policy.yaml | 2 +- .../first_match_nested_aggregate/policy.yaml | 4 +-- 10 files changed, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 8f9fa97..b54c390 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ graph TD Aggregate --> AggregateItem[Aggregate Choice] AggregateItem --> AggCondition[condition: CEL Bool Expression] - AggregateItem --> Emit[emit: CEL Expression] + AggregateItem --> AggOutput[output: CEL Expression] AggregateItem --> AggSubRule[rule: RuleBlock] ``` @@ -63,7 +63,7 @@ graph TD strategies: top-down `FIRST_MATCH` sequence (`match`), where the first condition to evaluate to `true` determines the single outcome, and `AGGREGATE` sequence (`aggregate`), where all choices with matching - conditions evaluate and accumulate their emitted values into a list. + conditions evaluate and accumulate their output values into a list. * **Strong Composition and Type Checking**: The policy compiler statically validates that all possible output paths evaluate to the **exact same type**, avoiding dynamic runtime type mismatches. @@ -110,7 +110,7 @@ A `rule` block supports the following fields: - `match` *(list)*: Sequential choices evaluated using `FIRST_MATCH` semantics (evaluates top-down until a condition is met). - `aggregate` *(list)*: Choices evaluated using `AGGREGATE` semantics - (evaluates all matching choices and collects emitted values into a list). + (evaluates all matching choices and collects output values into a list). --- @@ -171,7 +171,7 @@ Each aggregate choice item contains: omitted, it defaults to `true`. Conditions must not evaluate to a static constant `false`. - **Outcome**: Each aggregate choice item must define exactly one of: - * `emit` *(string)*: A CEL expression defining a value to append to the + * `output` *(string)*: A CEL expression defining a value to append to the accumulated result list if matched. * `rule` *(object)*: A nested `rule` block (such as a nested `match` block) to evaluate further if matched. @@ -187,7 +187,7 @@ Each aggregate choice item contains: A `condition` expression must type-check to a `bool` return type. When a `condition` predicate evaluates to `true`, the corresponding outcome -(`output`, `emit`, or nested `rule`) is evaluated. +(`output` or nested `rule`) is evaluated. #### Return Types for `match` Rules (Optional & Plain Types) For `match` rules, the return type is determined by evaluation completeness: @@ -209,7 +209,7 @@ For more details on CEL optionals, refer to the #### Return Types for `aggregate` Rules (List Types) For `aggregate` rules, matching outcomes are collected into a list: -- **Aggregated Return**: If the emitted items in an `aggregate` rule evaluate to +- **Aggregated Return**: If the output items in an `aggregate` rule evaluate to type `T`, the overall return type of the rule is `list(T)` (e.g., `list(string)`). - **Empty Result**: If no conditions within an `aggregate` block evaluate to @@ -218,9 +218,9 @@ For `aggregate` rules, matching outcomes are collected into a list: block) under an `aggregate` choice yields `optional.none()` (because no match branch was met), that `optional.none()` is pruned (omitted) from the aggregated list. -- **Nested List Values**: If an `emit` or nested `output` explicitly yields a - list value `list(T)` (e.g., `emit: "['tag1', 'tag2']"`), each emitted list is - appended as an element of the result list, yielding `list(list(T))` +- **Nested List Values**: If an `output` explicitly yields a list value + `list(T)` (e.g., `output: "['tag1', 'tag2']"`), each output list is appended + as an element of the result list, yielding `list(list(T))` (e.g., `[['tag1', 'tag2']]`). For conformance test examples, see: @@ -365,11 +365,11 @@ cases that must fail compilation with appropriate error sets. The suite covers the following compile-time checks: -1. **Type Agreement (Incompatible Outputs & Emits)**: The compiler must +1. **Type Agreement (Incompatible Outputs)**: The compiler must statically verify that all possible outcome branches in a policy evaluate - to the **exact same type**. Mixing outcome types in `match` outputs or - `aggregate` emits (e.g., one branch emitting `string` and another emitting - `int`) is a compile-time error. See + to the **exact same type**. Mixing outcome types in match or aggregate + outputs (e.g., one branch outputting `string` and another outputting `int`) + is a compile-time error. See [compose_conflicting_output](conformance/testdata/compile_errors/compose_conflicting_output/policy.yaml) and [aggregate_heterogeneous_outputs](conformance/testdata/compile_errors/aggregate_heterogeneous_outputs/policy.yaml). diff --git a/conformance/testdata/aggregate/policy.yaml b/conformance/testdata/aggregate/policy.yaml index 6fa760f..bfdf375 100644 --- a/conformance/testdata/aggregate/policy.yaml +++ b/conformance/testdata/aggregate/policy.yaml @@ -16,9 +16,9 @@ name: aggregate rule: aggregate: - condition: "resource.is_pii == true" - emit: "'PII'" + output: "'PII'" - condition: "resource.is_confidential == true" - emit: "'CONFIDENTIAL'" + output: "'CONFIDENTIAL'" - condition: "true" rule: match: diff --git a/conformance/testdata/aggregate_explicit_list_output/policy.yaml b/conformance/testdata/aggregate_explicit_list_output/policy.yaml index e75ef8c..351c0a1 100644 --- a/conformance/testdata/aggregate_explicit_list_output/policy.yaml +++ b/conformance/testdata/aggregate_explicit_list_output/policy.yaml @@ -16,4 +16,4 @@ name: aggregate_explicit_list_output rule: aggregate: - condition: "true" - emit: "['tag1', 'tag2']" + output: "['tag1', 'tag2']" diff --git a/conformance/testdata/aggregate_explicit_optional_none/policy.yaml b/conformance/testdata/aggregate_explicit_optional_none/policy.yaml index dc6ba7f..63ffafa 100644 --- a/conformance/testdata/aggregate_explicit_optional_none/policy.yaml +++ b/conformance/testdata/aggregate_explicit_optional_none/policy.yaml @@ -16,4 +16,4 @@ name: aggregate_explicit_optional_none rule: aggregate: - condition: "true" - emit: "optional.none()" + output: "optional.none()" diff --git a/conformance/testdata/aggregate_shadowed_variables/policy.yaml b/conformance/testdata/aggregate_shadowed_variables/policy.yaml index 2785c37..a91dc0b 100644 --- a/conformance/testdata/aggregate_shadowed_variables/policy.yaml +++ b/conformance/testdata/aggregate_shadowed_variables/policy.yaml @@ -19,7 +19,7 @@ rule: expression: "10" aggregate: - condition: "resource.cond1 == true" - emit: "variables.x" + output: "variables.x" - condition: "resource.cond2 == true" rule: variables: diff --git a/conformance/testdata/compile_errors/aggregate_false_condition/policy.yaml b/conformance/testdata/compile_errors/aggregate_false_condition/policy.yaml index a33d8c0..76f2194 100644 --- a/conformance/testdata/compile_errors/aggregate_false_condition/policy.yaml +++ b/conformance/testdata/compile_errors/aggregate_false_condition/policy.yaml @@ -16,4 +16,4 @@ name: aggregate_false_condition rule: aggregate: - condition: "false" - emit: "'FALSE'" + output: "'FALSE'" diff --git a/conformance/testdata/compile_errors/aggregate_heterogeneous_outputs/policy.yaml b/conformance/testdata/compile_errors/aggregate_heterogeneous_outputs/policy.yaml index b88dac9..afe07af 100644 --- a/conformance/testdata/compile_errors/aggregate_heterogeneous_outputs/policy.yaml +++ b/conformance/testdata/compile_errors/aggregate_heterogeneous_outputs/policy.yaml @@ -16,8 +16,8 @@ name: aggregate_heterogeneous_outputs rule: aggregate: - condition: "true" - emit: "'PII'" + output: "'PII'" - condition: "true" - emit: "403" + output: "403" - condition: "true" - emit: "{'reason': 'blocked'}" + output: "{'reason': 'blocked'}" diff --git a/conformance/testdata/compile_errors/aggregate_nested_mixed_semantics/policy.yaml b/conformance/testdata/compile_errors/aggregate_nested_mixed_semantics/policy.yaml index 8cdca5a..7a4c5ce 100644 --- a/conformance/testdata/compile_errors/aggregate_nested_mixed_semantics/policy.yaml +++ b/conformance/testdata/compile_errors/aggregate_nested_mixed_semantics/policy.yaml @@ -22,4 +22,4 @@ rule: rule: aggregate: - condition: "true" - emit: "'nested'" + output: "'nested'" diff --git a/conformance/testdata/compile_errors/unreachable_under_unconditional_aggregate_subrule/policy.yaml b/conformance/testdata/compile_errors/unreachable_under_unconditional_aggregate_subrule/policy.yaml index 0ba59c1..70d2fd3 100644 --- a/conformance/testdata/compile_errors/unreachable_under_unconditional_aggregate_subrule/policy.yaml +++ b/conformance/testdata/compile_errors/unreachable_under_unconditional_aggregate_subrule/policy.yaml @@ -19,6 +19,6 @@ rule: rule: aggregate: - condition: "1 == 2" - emit: "'ADMIN'" + output: "'ADMIN'" - condition: "true" output: "['FALLBACK']" diff --git a/conformance/testdata/first_match_nested_aggregate/policy.yaml b/conformance/testdata/first_match_nested_aggregate/policy.yaml index 8db0ca0..ecafd1f 100644 --- a/conformance/testdata/first_match_nested_aggregate/policy.yaml +++ b/conformance/testdata/first_match_nested_aggregate/policy.yaml @@ -19,8 +19,8 @@ rule: rule: aggregate: - condition: "cond2" - emit: "'A'" + output: "'A'" - condition: "cond3" - emit: "'B'" + output: "'B'" - condition: "true" output: "['FALLBACK']"