Switch invocation policy enforcer to use behavior enum instead of the old boolean flags.

We have recently added a new enum to express the intended behavior for `SetValue` invocation policy entries. This enum replaces the `overridable` and `append` boolean flags and offers significant advantages over those -- does not have a usable default value, thus must be explicitly set, and does not allow combination of `overridable && append` which is semantically contradictory (`overridable` only applies to non-repetable flags and `append` to repeatable flags).

Remove the boolean flags which are now replaced by the new enum and use the enum instead of the boolean flags in invocation policy enforcer.

PiperOrigin-RevId: 403372828
diff --git a/src/main/java/com/google/devtools/common/options/InvocationPolicyEnforcer.java b/src/main/java/com/google/devtools/common/options/InvocationPolicyEnforcer.java
index 886793c..136769b 100644
--- a/src/main/java/com/google/devtools/common/options/InvocationPolicyEnforcer.java
+++ b/src/main/java/com/google/devtools/common/options/InvocationPolicyEnforcer.java
@@ -13,7 +13,6 @@
 // limitations under the License.
 package com.google.devtools.common.options;
 
-import static java.util.concurrent.TimeUnit.MINUTES;
 import static java.util.stream.Collectors.joining;
 
 import com.google.common.base.Verify;
@@ -92,6 +91,7 @@
     }
   }
 
+  @Nullable
   public InvocationPolicy getInvocationPolicy() {
     return invocationPolicy;
   }
@@ -121,15 +121,6 @@
       return;
     }
 
-    // TODO(b/186167747): Remove the warning once we migrate to the new enum.
-    invocationPolicy.getFlagPoliciesList().stream()
-        .filter(p -> p.hasSetValue() && p.getSetValue().getBehavior() == Behavior.UNDEFINED)
-        .findFirst()
-        .ifPresent(
-            policy ->
-                logger.atWarning().atMostEvery(5, MINUTES).log(
-                    "Invocation policy has missing/undefined behavior: %s", policy));
-
     // The effective policy returned is expanded, filtered for applicable commands, and cleaned of
     // redundancies and conflicts.
     List<FlagPolicyWithContext> effectivePolicies =
@@ -314,6 +305,13 @@
         String.format("Disallow_Values on expansion flags like %s is not allowed.", flagName));
   }
 
+  private static OptionsParsingException throwUndefinedBehaviorException(FlagPolicy policy)
+      throws OptionsParsingException {
+    throw new OptionsParsingException(
+        String.format(
+            "SetValue operation from invocation policy for has an undefined behavior: %s", policy));
+  }
+
   /**
    * Expand a single policy. If the policy is not about an expansion flag, this will simply return a
    * list with a single element, oneself. If the policy is for an expansion flag, the policy will
@@ -425,7 +423,8 @@
       OptionDescription subflagDesc,
       List<String> subflagValue,
       OptionInstanceOrigin subflagOrigin,
-      FlagPolicyWithContext originalPolicy) {
+      FlagPolicyWithContext originalPolicy)
+      throws OptionsParsingException {
     // Some checks.
     OptionDefinition subflag = subflagDesc.getOptionDefinition();
     Verify.verify(originalPolicy.policy.getOperationCase().equals(OperationCase.SET_VALUE));
@@ -439,10 +438,18 @@
     for (String value : subflagValue) {
       setValueExpansion.addFlagValue(value);
     }
-    if (subflag.allowsMultiple()) {
-      setValueExpansion.setAppend(originalPolicy.policy.getSetValue().getOverridable());
-    } else {
-      setValueExpansion.setOverridable(originalPolicy.policy.getSetValue().getOverridable());
+
+    switch (originalPolicy.policy.getSetValue().getBehavior()) {
+      case UNDEFINED:
+        throw throwUndefinedBehaviorException(originalPolicy.policy);
+      case FINAL_VALUE_IGNORE_OVERRIDES:
+      case APPEND:
+        setValueExpansion.setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES);
+        break;
+      case ALLOW_OVERRIDES:
+        setValueExpansion.setBehavior(
+            subflag.allowsMultiple() ? Behavior.APPEND : Behavior.ALLOW_OVERRIDES);
+        break;
     }
 
     // Commands from the original policy, flag name of the expansion
@@ -554,43 +561,51 @@
               optionDefinition));
     }
 
-    if (setValue.getOverridable() && valueDescription != null) {
-      // The user set the value for the flag but the flag policy is overridable, so keep the user's
-      // value.
-      logger.at(loglevel).log(
-          "Keeping value '%s' from source '%s' for %s because the invocation policy specifying "
-              + "the value(s) '%s' is overridable",
-          valueDescription.getValue(),
-          valueDescription.getSourceString(),
-          optionDefinition,
-          setValue.getFlagValueList());
-    } else {
-
-      if (!setValue.getAppend()) {
+    switch (setValue.getBehavior()) {
+      case UNDEFINED:
+        throw throwUndefinedBehaviorException(flagPolicy.policy);
+      case ALLOW_OVERRIDES:
+        if (valueDescription != null) {
+          // The user set the value for the flag but the flag policy is overridable, so keep the
+          // user's
+          // value.
+          logger.at(loglevel).log(
+              "Keeping value '%s' from source '%s' for %s because the invocation policy specifying "
+                  + "the value(s) '%s' is overridable",
+              valueDescription.getValue(),
+              valueDescription.getSourceString(),
+              optionDefinition,
+              setValue.getFlagValueList());
+          // Nothing to do -- the value already has an override.
+          return;
+        }
+        break;
+      case FINAL_VALUE_IGNORE_OVERRIDES:
         // Clear the value in case the flag is a repeated flag so that values don't accumulate.
         parser.clearValue(flagPolicy.description.getOptionDefinition());
-      }
+        break;
+      case APPEND:
+        break;
+    }
 
-      // Set all the flag values from the policy.
-      for (String flagValue : setValue.getFlagValueList()) {
-        if (valueDescription == null) {
-          logger.at(loglevel).log(
-              "Setting value for %s from invocation policy to '%s', overriding the default value "
-                  + "'%s'",
-              optionDefinition, flagValue, optionDefinition.getDefaultValue());
-        } else {
-          logger.at(loglevel).log(
-              "Setting value for %s from invocation policy to '%s', overriding value '%s' from "
-                  + "'%s'",
-              optionDefinition,
-              flagValue,
-              valueDescription.getValue(),
-              valueDescription.getSourceString());
-        }
+    // Set all the flag values from the policy.
+    for (String flagValue : setValue.getFlagValueList()) {
+      if (valueDescription == null) {
+        logger.at(loglevel).log(
+            "Setting value for %s from invocation policy to '%s', overriding the default value "
+                + "'%s'",
+            optionDefinition, flagValue, optionDefinition.getDefaultValue());
+      } else {
+        logger.at(loglevel).log(
+            "Setting value for %s from invocation policy to '%s', overriding value '%s' from '%s'",
+            optionDefinition,
+            flagValue,
+            valueDescription.getValue(),
+            valueDescription.getSourceString());
+      }
 
         parser.setOptionValueAtSpecificPriorityWithoutExpansion(
             flagPolicy.origin, optionDefinition, flagValue);
-      }
     }
   }
 
diff --git a/src/main/protobuf/invocation_policy.proto b/src/main/protobuf/invocation_policy.proto
index 38869e7..3f1576f 100644
--- a/src/main/protobuf/invocation_policy.proto
+++ b/src/main/protobuf/invocation_policy.proto
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 syntax = "proto2";
+
 package blaze.invocation_policy;
 
 // option java_api_version = 2;
@@ -100,18 +101,8 @@
   // may fail to parse or result in an unexpected value.
   repeated string flag_value = 1;
 
-  // Whether to allow this policy to be overridden by user-specified values.
-  // When set, if the user specified a value for this flag, use the value
-  // from the user, otherwise use the value specified in this policy.
-  // This value is redundant to behavior -- please keep it in sync with it.
-  optional bool overridable = 2;
-
-  // If true, and if the flag named in the policy is a repeatable flag, then
-  // the values listed in flag_value do not replace all the user-set or default
-  // values of the flag, but instead append to them. If the flag is not
-  // repeatable, then this has no effect.
-  // This value is redundant to behavior -- please keep it in sync with it.
-  optional bool append = 3;
+  // Obsolete overridable and append flags, replaced by behavior.
+  reserved 2, 3;
 
   enum Behavior {
     UNDEFINED = 0;
@@ -134,8 +125,6 @@
   // For the time being, it coexists with overridable and append with duplicate
   // semantics.Please fill both of the values as we migrate to use behavior
   // only.
-  // TODO(b/186167747): Deprecate and remove append and overridable flag in
-  // favor of this one.
   optional Behavior behavior = 4;
 }
 
diff --git a/src/test/java/com/google/devtools/common/options/BUILD b/src/test/java/com/google/devtools/common/options/BUILD
index 2e489c5..ab1c422 100644
--- a/src/test/java/com/google/devtools/common/options/BUILD
+++ b/src/test/java/com/google/devtools/common/options/BUILD
@@ -55,5 +55,6 @@
         "//third_party:junit4",
         "//third_party:mockito",
         "//third_party:truth",
+        "@com_google_testparameterinjector//:testparameterinjector",
     ],
 )
diff --git a/src/test/java/com/google/devtools/common/options/InvocationPolicyBreakingConditionsTest.java b/src/test/java/com/google/devtools/common/options/InvocationPolicyBreakingConditionsTest.java
index afbdfbc..a54ba08 100644
--- a/src/test/java/com/google/devtools/common/options/InvocationPolicyBreakingConditionsTest.java
+++ b/src/test/java/com/google/devtools/common/options/InvocationPolicyBreakingConditionsTest.java
@@ -16,6 +16,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
+import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.SetValue.Behavior;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -59,11 +60,13 @@
         .addFlagPoliciesBuilder()
         .setFlagName("i_do_not_exist")
         .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
         .addFlagValue(TEST_STRING_POLICY_VALUE);
     invocationPolicyBuilder
         .addFlagPoliciesBuilder()
         .setFlagName("test_string")
         .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
         .addFlagValue(TEST_STRING_POLICY_VALUE_2);
 
     InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
diff --git a/src/test/java/com/google/devtools/common/options/InvocationPolicyMiscTest.java b/src/test/java/com/google/devtools/common/options/InvocationPolicyMiscTest.java
index 316b528..e7c4f4e 100644
--- a/src/test/java/com/google/devtools/common/options/InvocationPolicyMiscTest.java
+++ b/src/test/java/com/google/devtools/common/options/InvocationPolicyMiscTest.java
@@ -16,6 +16,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
+import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.SetValue.Behavior;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -61,6 +62,7 @@
         .addFlagPoliciesBuilder()
         .setFlagName("test_deprecated")
         .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
         .addFlagValue(TEST_DEPRECATED_POLICY_VALUE);
     InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
 
@@ -98,6 +100,7 @@
         .addFlagPoliciesBuilder()
         .setFlagName("test_deprecated")
         .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
         .addFlagValue(TEST_DEPRECATED_POLICY_VALUE);
     InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
 
diff --git a/src/test/java/com/google/devtools/common/options/InvocationPolicySetValueTest.java b/src/test/java/com/google/devtools/common/options/InvocationPolicySetValueTest.java
index e5ca5c4..3320c46 100644
--- a/src/test/java/com/google/devtools/common/options/InvocationPolicySetValueTest.java
+++ b/src/test/java/com/google/devtools/common/options/InvocationPolicySetValueTest.java
@@ -18,34 +18,36 @@
 import static org.junit.Assert.assertThrows;
 
 import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
+import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.SetValue;
+import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.SetValue.Behavior;
+import com.google.testing.junit.testparameterinjector.TestParameter;
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
+import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 
 /** Test InvocationPolicies with the SetValues operation. */
-@RunWith(JUnit4.class)
+@RunWith(TestParameterInjector.class)
 public class InvocationPolicySetValueTest extends InvocationPolicyEnforcerTestBase {
 
-  // Useful constants
   public static final String BUILD_COMMAND = "build";
   public static final String TEST_STRING_USER_VALUE = "user value";
   public static final String TEST_STRING_USER_VALUE_2 = "user value 2";
   public static final String TEST_STRING_POLICY_VALUE = "policy value";
   public static final String TEST_STRING_POLICY_VALUE_2 = "policy value 2";
 
-  /**
-   * Tests that policy overrides a value when that value is from the user.
-   */
+  /** Tests that policy overwrites a value when that value is from the user. */
   @Test
-  public void testSetValueOverridesUser() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void finalValueIgnoreOverrides_overwritesUserSetting() throws Exception {
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("test_string")
         .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
         .addFlagValue(TEST_STRING_POLICY_VALUE);
 
-    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
     parser.parse("--test_string=" + TEST_STRING_USER_VALUE);
 
     TestOptions testOptions = getTestOptions();
@@ -65,20 +67,21 @@
   }
 
   /**
-   * Tests that policy overrides a value when the user doesn't specify the value (i.e., the value
+   * Tests that policy overwrites a value when the user doesn't specify the value (i.e., the value
    * is from the flag's default from its definition).
    */
   @Test
-  public void testSetValueOverridesDefault() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void finalValueIgnoreOverrides_overwritesDefault() throws Exception {
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("test_string")
         .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
         .addFlagValue(TEST_STRING_POLICY_VALUE);
 
     // No user value.
-    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
 
     // All the flags should be their default value.
     TestOptions testOptions = getTestOptions();
@@ -91,20 +94,20 @@
     assertThat(testOptions.testString).isEqualTo(TEST_STRING_POLICY_VALUE);
   }
 
-  /**
-   * Tests that SetValue overrides the user's value when the flag allows multiple values.
-   */
+  /** Tests that SetValue overwrites the user's value when the flag allows multiple values. */
   @Test
-  public void testSetValueWithMultipleValuesOverridesUser() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void finalValueIgnoreOverrides_flagWithMultipleValues_overwritesUserSetting()
+      throws Exception {
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("test_multiple_string")
         .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
         .addFlagValue(TEST_STRING_POLICY_VALUE)
         .addFlagValue(TEST_STRING_POLICY_VALUE_2);
 
-    InvocationPolicyEnforcer enforcer =  createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
     parser.parse(
         "--test_multiple_string=" + TEST_STRING_USER_VALUE,
         "--test_multiple_string=" + TEST_STRING_USER_VALUE_2);
@@ -125,21 +128,23 @@
   }
 
   /**
-   * Tests that policy overrides the default value when the flag allows multiple values and the user
-   * doesn't provide a value.
+   * Tests that policy overwrites the default value when the flag allows multiple values and the
+   * user doesn't provide a value.
    */
   @Test
-  public void testSetValueWithMultipleValuesOverridesDefault() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void finalValueIgnoreOverrides_flagWithMultipleValues_overwritesDefault()
+      throws Exception {
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("test_multiple_string")
         .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
         .addFlagValue(TEST_STRING_POLICY_VALUE)
         .addFlagValue(TEST_STRING_POLICY_VALUE_2);
 
     // No user value.
-    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
 
     // Repeatable flags always default to the empty list.
     TestOptions testOptions = getTestOptions();
@@ -155,32 +160,35 @@
   }
 
   @Test
-  public void testSetValueHasMultipleValuesButFlagIsNotMultiple() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void setMultipleValuesForSingleValuedFlag_fails(@TestParameter Behavior behavior)
+      throws Exception {
+    Assume.assumeTrue(behavior != Behavior.UNDEFINED);
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("test_string") // Not repeatable flag.
         .getSetValueBuilder()
+        .setBehavior(behavior)
         .addFlagValue(TEST_STRING_POLICY_VALUE) // Has multiple values.
         .addFlagValue(TEST_STRING_POLICY_VALUE_2);
 
-    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
 
     assertThrows(OptionsParsingException.class, () -> enforcer.enforce(parser, BUILD_COMMAND));
   }
 
   @Test
-  public void testSetValueAppendsToMultipleValuedFlag() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void append_appendsToMultipleValuedFlag() throws Exception {
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("test_multiple_string")
         .getSetValueBuilder()
+        .setBehavior(Behavior.APPEND)
         .addFlagValue(TEST_STRING_POLICY_VALUE)
-        .addFlagValue(TEST_STRING_POLICY_VALUE_2)
-        .setAppend(true);
+        .addFlagValue(TEST_STRING_POLICY_VALUE_2);
 
-    InvocationPolicyEnforcer enforcer =  createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
     parser.parse(
         "--test_multiple_string=" + TEST_STRING_USER_VALUE,
         "--test_multiple_string=" + TEST_STRING_USER_VALUE_2);
@@ -205,14 +213,16 @@
   }
 
   @Test
-  public void testSetValueWithExpansionFlags() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void finalValueIgnoreOverrides_setFlagWithExpansion_setsExpandedValues() throws Exception {
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("test_expansion")
-        .getSetValueBuilder(); // This value must be empty for a Void flag.
+        // SetValue must have no values for a Void flag.
+        .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES);
 
-    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
     // Unrelated flag, but --test_expansion is not set
     parser.parse("--test_string=throwaway value");
 
@@ -234,15 +244,16 @@
   }
 
   @Test
-  public void testSetValueWithExpansionFlagOnExpansionFlag() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void finalValueIgnoreOverrides_flagExpandingToExpansion_setsRecursiveValues()
+      throws Exception {
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("test_recursive_expansion_top_level")
-        .getSetValueBuilder();
+        .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES);
 
-
-    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
     // Unrelated flag, but --test_expansion is not set
     parser.parse("--test_string=throwaway value");
 
@@ -264,16 +275,16 @@
   }
 
   @Test
-  public void testOverridableSetValueWithExpansionFlags() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void allowOverrides_setFlagWithExpansion_keepsUserSpecifiedFlag() throws Exception {
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("test_expansion")
         .getSetValueBuilder()
-        .addFlagValue("") // this value is arbitrary, the value for a Void flag is ignored
-        .setOverridable(true);
+        .setBehavior(Behavior.ALLOW_OVERRIDES)
+        .addFlagValue(""); // this value is arbitrary, the value for a Void flag is ignored
 
-    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
     // Unrelated flag, but --test_expansion is not set
     parser.parse("--expanded_c=23");
 
@@ -297,15 +308,16 @@
   }
 
   @Test
-  public void testOverridableSetValueWithExpansionToRepeatingFlag() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void allowOverrides_flagExpandingToRepeatingFlag_appendsRepeatedValues() throws Exception {
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("test_expansion_to_repeatable")
-        .getSetValueBuilder() // This value must be empty for a Void flag.
-        .setOverridable(true);
+        .getSetValueBuilder()
+        // SetValue must have no values for a Void flag.
+        .setBehavior(Behavior.ALLOW_OVERRIDES);
 
-    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
     // Unrelated flag, but --test_expansion is not set
     parser.parse("--test_multiple_string=foo");
 
@@ -324,15 +336,17 @@
   }
 
   @Test
-  public void testNonOverridableSetValueWithExpansionFlags() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void finalValueIgnoreOverrides_setFlagWithExpansion_overwritesUserSettingForExpandedFlag()
+      throws Exception {
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("test_expansion")
-        .getSetValueBuilder() // This value must be empty for a Void flag.
-        .setOverridable(false);
+        // SetValue must have no values for a Void flag.
+        .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES);
 
-    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
     // Unrelated flag, but --test_expansion is not set
     parser.parse("--expanded_c=23");
 
@@ -357,13 +371,15 @@
   }
 
   @Test
-  public void testNonOverridableSetValueWithExpansionToRepeatingFlag() throws Exception {
+  public void finalValueIgnoreOverrides_flagWithExpansionToRepeatingFlag_overwritesUserSetting()
+      throws Exception {
     InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
     invocationPolicyBuilder
         .addFlagPoliciesBuilder()
         .setFlagName("test_expansion_to_repeatable")
-        .getSetValueBuilder() // This value must be empty for a Void flag.
-        .setOverridable(false);
+        // SetValue must have no values for a Void flag.
+        .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES);
 
     InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
     // Unrelated flag, but --test_expansion is not set
@@ -371,29 +387,26 @@
 
     // The flags that --test_expansion expands into should still be their default values
     // except for the explicitly marked flag.
-    TestOptions testOptions = getTestOptions();
-    assertThat(testOptions.testMultipleString).contains("foo");
+    assertThat(getTestOptions().testMultipleString).contains("foo");
 
     enforcer.enforce(parser, "build");
 
     // After policy enforcement, the flag should no longer have the user's value.
-    testOptions = getTestOptions();
-    assertThat(testOptions.testMultipleString).doesNotContain("foo");
-    assertThat(testOptions.testMultipleString).contains(TestOptions.EXPANDED_MULTIPLE_1);
-    assertThat(testOptions.testMultipleString).contains(TestOptions.EXPANDED_MULTIPLE_2);
+    assertThat(getTestOptions().testMultipleString)
+        .containsExactly(TestOptions.EXPANDED_MULTIPLE_1, TestOptions.EXPANDED_MULTIPLE_2);
   }
 
-
   @Test
-  public void testSetValueWithExpandedFlags() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void finalValueIgnoreOverrides_overwritesUserSettingFromExpandedFlag() throws Exception {
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("expanded_c")
         .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
         .addFlagValue("64");
 
-    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
     parser.parse("--test_expansion");
 
     // --test_expansion should set the values from its expansion
@@ -415,15 +428,17 @@
   }
 
   @Test
-  public void testSetValueOfImplicitlyRequiredFlags() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void finalValueIgnoreOverrides_overwritesImplicitRequirementFromUserSetting()
+      throws Exception {
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("implicit_requirement_a")
         .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
         .addFlagValue(TEST_STRING_POLICY_VALUE);
 
-    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
     parser.parse("--test_implicit_requirement=" + TEST_STRING_USER_VALUE);
 
     // test_implicit_requirement sets implicit_requirement_a to "foo"
@@ -446,6 +461,7 @@
         .addFlagPoliciesBuilder()
         .setFlagName("test_implicit_requirement")
         .getSetValueBuilder()
+        .setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES)
         .addFlagValue(TEST_STRING_POLICY_VALUE);
     InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
     parser.parse("--implicit_requirement_a=" + TEST_STRING_USER_VALUE);
@@ -461,16 +477,16 @@
   }
 
   @Test
-  public void testSetValueOverridable() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void allowOverrides_leavesUserSetting() throws Exception {
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("test_string")
         .getSetValueBuilder()
-        .addFlagValue(TEST_STRING_POLICY_VALUE)
-        .setOverridable(true);
+        .setBehavior(Behavior.ALLOW_OVERRIDES)
+        .addFlagValue(TEST_STRING_POLICY_VALUE);
 
-    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
     parser.parse("--test_string=" + TEST_STRING_USER_VALUE);
 
     TestOptions testOptions = getTestOptions();
@@ -485,13 +501,16 @@
   }
 
   @Test
-  public void testSetValueWithNoValueThrows() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder.addFlagPoliciesBuilder()
+  public void setFlagValueWithNoValue_fails(@TestParameter Behavior behavior) throws Exception {
+    Assume.assumeTrue(behavior != Behavior.UNDEFINED);
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
+        .addFlagPoliciesBuilder()
         .setFlagName("test_string")
-        .getSetValueBuilder(); // No value.
+        .getSetValueBuilder()
+        .setBehavior(behavior); // No value.
 
-    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
     parser.parse("--test_string=" + TEST_STRING_USER_VALUE);
 
     TestOptions testOptions = getTestOptions();
@@ -501,26 +520,60 @@
   }
 
   @Test
-  public void testConfigNotAllowed() throws Exception {
-    InvocationPolicy.Builder invocationPolicyBuilder = InvocationPolicy.newBuilder();
-    invocationPolicyBuilder
+  public void enforce_setValueWithUndefinedBehavior_fails(
+      @TestParameter boolean hasBehavior,
+      @TestParameter({"test_string", "test_expansion", "test_implicit_requirement"})
+          String flagName)
+      throws Exception {
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    SetValue.Builder setValue =
+        invocationPolicy
+            .addFlagPoliciesBuilder()
+            .setFlagName(flagName)
+            .getSetValueBuilder()
+            .addFlagValue("any value");
+    if (hasBehavior) {
+      setValue.setBehavior(Behavior.UNDEFINED);
+    }
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
+
+    OptionsParsingException e =
+        assertThrows(OptionsParsingException.class, () -> enforcer.enforce(parser, BUILD_COMMAND));
+    assertThat(e)
+        .hasMessageThat()
+        .startsWith(
+            String.format(
+                "SetValue operation from invocation policy for has an undefined behavior:"
+                    + " flag_name: \"%s\"\n"
+                    + "set_value {\n",
+                flagName));
+  }
+
+  @Test
+  public void enforce_policySettingConfig_fails(@TestParameter Behavior behavior) throws Exception {
+    Assume.assumeTrue(behavior != Behavior.UNDEFINED);
+    InvocationPolicy.Builder invocationPolicy = InvocationPolicy.newBuilder();
+    invocationPolicy
         .addFlagPoliciesBuilder()
         .setFlagName("config")
         .getSetValueBuilder()
+        .setBehavior(behavior)
         .addFlagValue("foo");
 
-    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicyBuilder);
+    InvocationPolicyEnforcer enforcer = createOptionsPolicyEnforcer(invocationPolicy);
     parser.parse();
     OptionsParsingException expected =
         assertThrows(OptionsParsingException.class, () -> enforcer.enforce(parser, BUILD_COMMAND));
     assertThat(expected)
         .hasMessageThat()
-        .isEqualTo(
+        .startsWith(
             "Invocation policy is applied after --config expansion, changing config values now "
                 + "would have no effect and is disallowed to prevent confusion. Please remove "
                 + "the following policy : flag_name: \"config\"\n"
                 + "set_value {\n"
                 + "  flag_value: \"foo\"\n"
-                + "}\n");
+                + "  behavior: "
+                + behavior
+                + "\n");
   }
 }